Как я могу автоматически поднять свой пакетный файл, чтобы он запрашивал права администратора UAC, если это необходимо?

Я хочу, чтобы мой командный файл работал только с повышенными правами. Если уровень не повышен, предоставьте пользователю возможность перезапустить пакет с повышенными правами.

Я пишу командный файл, чтобы установить системную переменную, скопировать два файла в папку Program Files и запустить установщик драйвера. Если пользователь Windows 7/Windows Vista ( UAC включен и даже если он является локальным администратором) запускает его без щелчка правой кнопкой мыши и выбора "Запуск от имени администратора", он получит "Отказано в доступе", скопировав два файла и записав системную переменную.,

Я хотел бы использовать команду для автоматического перезапуска пакета с повышенными правами, если пользователь фактически является администратором. В противном случае, если они не являются администратором, я хочу сказать им, что им нужны права администратора для запуска командного файла. Я использую xcopy для копирования файлов и REG ADD для записи системной переменной. Я использую эти команды для работы с возможными компьютерами с Windows XP. Я нашел похожие вопросы по этой теме, но ничего, что касалось перезапуска командного файла с повышенными правами.

19 ответов

Решение

Вы можете сделать так, чтобы скрипт сам вызывал psexec-h Возможность запуска с повышенными правами.

Я не уверен, как вы могли бы определить, работает ли он с повышенными правами или нет... может быть, повторите попытку с повышенными привилегиями только при наличии ошибки "Отказано в доступе"?

Или вы можете просто иметь команды для xcopy а также reg.exe всегда бегать с psexec -h, но это будет раздражать конечного пользователя, если ему нужно будет каждый раз вводить свой пароль (или небезопасно, если вы включите пароль в скрипт)...

Существует простой способ без необходимости использования внешнего инструмента - он отлично работает с Windows 7, 8, 8.1 и 10 и также обратно совместим (Windows XP не имеет UAC, поэтому повышение прав не требуется - в этом на всякий случай сценарий просто продолжается).

Проверьте этот код (я был вдохновлен кодом от NIronwolf, размещенным в ветке Batch File - "Доступ запрещен" в Windows 7?), Но я улучшил его - в моей версии нет никакого каталога, созданного и удаленного для проверьте наличие прав администратора):

::::::::::::::::::::::::::::::::::::::::::::
:: Elevate.cmd - Version 4
:: Automatically check & get admin rights
:: see "https://stackru.com/a/12264592/1016343" for description
::::::::::::::::::::::::::::::::::::::::::::
 @echo off
 CLS
 ECHO.
 ECHO =============================
 ECHO Running Admin shell
 ECHO =============================

:init
 setlocal DisableDelayedExpansion
 set cmdInvoke=1
 set winSysFolder=System32
 set "batchPath=%~0"
 for %%k in (%0) do set batchName=%%~nk
 set "vbsGetPrivileges=%temp%\OEgetPriv_%batchName%.vbs"
 setlocal EnableDelayedExpansion

:checkPrivileges
  NET FILE 1>NUL 2>NUL
  if '%errorlevel%' == '0' ( goto gotPrivileges ) else ( goto getPrivileges )

:getPrivileges
  if '%1'=='ELEV' (echo ELEV & shift /1 & goto gotPrivileges)
  ECHO.
  ECHO **************************************
  ECHO Invoking UAC for Privilege Escalation
  ECHO **************************************

  ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%"
  ECHO args = "ELEV " >> "%vbsGetPrivileges%"
  ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%"
  ECHO args = args ^& strArg ^& " "  >> "%vbsGetPrivileges%"
  ECHO Next >> "%vbsGetPrivileges%"

  if '%cmdInvoke%'=='1' goto InvokeCmd 

  ECHO UAC.ShellExecute "!batchPath!", args, "", "runas", 1 >> "%vbsGetPrivileges%"
  goto ExecElevation

:InvokeCmd
  ECHO args = "/c """ + "!batchPath!" + """ " + args >> "%vbsGetPrivileges%"
  ECHO UAC.ShellExecute "%SystemRoot%\%winSysFolder%\cmd.exe", args, "", "runas", 1 >> "%vbsGetPrivileges%"

:ExecElevation
 "%SystemRoot%\%winSysFolder%\WScript.exe" "%vbsGetPrivileges%" %*
 exit /B

:gotPrivileges
 setlocal & cd /d %~dp0
 if '%1'=='ELEV' (del "%vbsGetPrivileges%" 1>nul 2>nul  &  shift /1)

 ::::::::::::::::::::::::::::
 ::START
 ::::::::::::::::::::::::::::
 REM Run shell as admin (example) - put here code as you like
 ECHO %batchName% Arguments: P1=%1 P2=%2 P3=%3 P4=%4 P5=%5 P6=%6 P7=%7 P8=%8 P9=%9
 cmd /k

Сценарий использует тот факт, что NET FILE требует прав администратора и возвращает errorlevel 1 если у вас его нет Повышение достигается путем создания сценария, который повторно запускает пакетный файл для получения привилегий. Это заставляет Windows представить диалоговое окно UAC и запрашивает учетную запись администратора и пароль.

Я проверил его с Windows 7, 8, 8.1, 10 и с Windows XP - он отлично работает для всех. Преимущество заключается в том, что после начальной точки вы можете разместить все, что требует привилегий системного администратора, например, если вы собираетесь переустановить и повторно запустить службу Windows для целей отладки (предполагается, что mypackage.msi является пакетом установки службы):

msiexec /passive /x mypackage.msi
msiexec /passive /i mypackage.msi
net start myservice

Без этого сценария повышения привилегий UAC трижды запрашивал у вас имя пользователя и пароль администратора - теперь вас спрашивают только один раз в начале и только при необходимости.


Если вашему сценарию просто нужно показать сообщение об ошибке и выйти, если у него нет прав администратора вместо автоматического повышения, это еще проще: это можно сделать, добавив следующее в начале сценария:

@ECHO OFF & CLS & ECHO.
NET FILE 1>NUL 2>NUL & IF ERRORLEVEL 1 (ECHO You must right-click and select &
  ECHO "RUN AS ADMINISTRATOR"  to run this batch. Exiting... & ECHO. &
  PAUSE & EXIT /D)
REM ... proceed here with admin rights ...

Таким образом, пользователь должен щелкнуть правой кнопкой мыши и выбрать "Запуск от имени администратора". Сценарий будет продолжен после REM Заявление, если он обнаружит права администратора, в противном случае выйдите с ошибкой. Если вам не требуется PAUSE Просто удали это. Важный: NET FILE [...] EXIT /D) должен быть на одной линии. Он отображается здесь в несколько строк для лучшей читаемости!


На некоторых машинах я столкнулся с проблемами, которые уже решены в новой версии выше. Одна из них была связана с другой обработкой двойных кавычек, а другая проблема была связана с тем, что UAC был отключен (установлен на самый низкий уровень) на компьютере с Windows 7, поэтому скрипт вызывает себя снова и снова.

Я исправил это сейчас, убрав кавычки в пути и добавив их позже, и добавил дополнительный параметр, который добавляется при повторном запуске сценария с повышенными правами.

Двойные кавычки удаляются следующим (подробности здесь):

setlocal DisableDelayedExpansion
set "batchPath=%~0"
setlocal EnableDelayedExpansion

Затем вы можете получить доступ к пути с помощью !batchPath!, Он не содержит двойных кавычек, поэтому можно с уверенностью сказать, "!batchPath!" позже в сценарии.

Линия

if '%1'=='ELEV' (shift & goto gotPrivileges)

проверяет, был ли сценарий уже вызван сценарием VBScript для повышения прав, что позволяет избежать бесконечных рекурсий. Удаляет параметр используя shift,


Обновить:

  • Чтобы избежать необходимости регистрировать .vbs расширение в Windows 10, я заменил строку
    "%temp%\OEgetPrivileges.vbs"
    от
    "%SystemRoot%\System32\WScript.exe" "%temp%\OEgetPrivileges.vbs"
    в сценарии выше; также добавил cd /d %~dp0 как предложено Стивеном (отдельный ответ) и Томашем Зато (комментарий), чтобы установить каталог скриптов по умолчанию.

  • Теперь скрипт учитывает параметры командной строки, передаваемые ему. Спасибо jxmallet, TanisDLJ и Питеру Мортенсену за наблюдения и вдохновение.

  • Согласно намёку Артёма Б., я его проанализировал и заменил SHIFT от SHIFT /1, который сохраняет имя файла для %0 параметр

  • добавленной del "%temp%\OEgetPrivileges_%batchName%.vbs" к :gotPrivileges раздел для очистки (как предложил mlt). добавленной %batchName% чтобы избежать воздействия, если вы запускаете разные партии параллельно. Обратите внимание, что вам нужно использовать for чтобы иметь возможность воспользоваться расширенными строковыми функциями, такими как %%~nk, который извлекает только имя файла.

  • Оптимизированная структура скрипта, улучшения (добавлена ​​переменная vbsGetPrivileges на который теперь ссылаются везде, что позволяет легко изменить путь или имя файла, только удалить .vbs файл, если партия должна быть повышена)

  • В некоторых случаях для повышения прав требовался другой синтаксис вызова. Если скрипт не работает, проверьте следующие параметры:
    set cmdInvoke=0
    set winSysFolder=System32
    Либо измените 1-й параметр на set cmdInvoke=1 и проверьте, если это уже решает проблему. Это добавит cmd.exe к сценарию выполнения возвышения.
    Или попробуйте изменить второй параметр на winSysFolder=Sysnative это может помочь (но в большинстве случаев не требуется) в 64-битных системах. (ADBailey сообщил об этом). "Sysnative" требуется только для запуска 64-разрядных приложений с 32-разрядного узла сценария (например, процесс сборки Visual Studio или вызов сценария из другого 32-разрядного приложения).

  • Чтобы было понятнее, как интерпретируются параметры, я теперь отображаю это как P1=value1 P2=value2 ... P9=value9, Это особенно полезно, если вам нужно заключить такие параметры, как пути, в двойные кавычки, например "C:\Program Files",

Как упоминали jcoder и Matt, PowerShell сделал это легко, и его даже можно было встроить в пакетный скрипт без создания нового скрипта.

Я изменил сценарий Мэтта:

:checkPrivileges 
NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto gotPrivileges 
) else ( powershell "saps -filepath %0 -verb runas" >nul 2>&1)
exit /b 

Там нет необходимости для :getPrivileges этикетка.

Я делаю это так:

NET SESSION
IF %ERRORLEVEL% NEQ 0 GOTO ELEVATE
GOTO ADMINTASKS

:ELEVATE
CD /d %~dp0
MSHTA "javascript: var shell = new ActiveXObject('shell.application'); shell.ShellExecute('%~nx0', '', '', 'runas', 1);close();"
EXIT

:ADMINTASKS
(Do whatever you need to do here)
EXIT

Таким образом, это просто и использовать только команды Windows по умолчанию. Это здорово, если вам нужно распространять пакетный файл.

CD /d %~dp0 Устанавливает текущую директорию в текущую директорию файла (если это не так, независимо от того, на каком диске находится файл, благодаря /d опция).

%~nx0 Возвращает текущее имя файла с расширением (если вы не включите расширение и в папке есть исполняемый файл с тем же именем, он вызовет исполняемый файл).

На это сообщение так много ответов, что я даже не знаю, увидят ли мой ответ.

Во всяком случае, я считаю, что этот способ проще, чем другие решения, предложенные в других ответах, я надеюсь, что это кому-то поможет.

Я использую отличный ответ Мэтта, но я вижу разницу между моими системами Windows 7 и Windows 8 при выполнении сценариев с повышенными правами.

Как только сценарий повышен в Windows 8, текущему каталогу присваивается значение C:\Windows\system32, К счастью, существует простой обходной путь, если изменить текущий каталог на путь к текущему сценарию:

cd /d %~dp0

Примечание: использовать cd /d чтобы убедиться, что буква диска также изменена.

Чтобы проверить это, вы можете скопировать следующее в скрипт. Запустите нормально на любой версии, чтобы увидеть тот же результат. Запустите от имени администратора и увидите разницу в Windows 8:

@echo off
echo Current path is %cd%
echo Changing directory to the path of the current script
cd %~dp0
echo Current path is %cd%
pause

У Мэтта отличный ответ, но он убирает все аргументы, переданные сценарию. Вот моя модификация, которая сохраняет аргументы. Я также включил исправление Стивена для проблемы с рабочим каталогом в Windows 8.

@ECHO OFF
setlocal EnableDelayedExpansion

NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto START ) else ( goto getPrivileges ) 

:getPrivileges
if '%1'=='ELEV' ( goto START )

set "batchPath=%~f0"
set "batchArgs=ELEV"

::Add quotes to the batch path, if needed
set "script=%0"
set script=%script:"=%
IF '%0'=='!script!' ( GOTO PathQuotesDone )
    set "batchPath=""%batchPath%"""
:PathQuotesDone

::Add quotes to the arguments, if needed.
:ArgLoop
IF '%1'=='' ( GOTO EndArgLoop ) else ( GOTO AddArg )
    :AddArg
    set "arg=%1"
    set arg=%arg:"=%
    IF '%1'=='!arg!' ( GOTO NoQuotes )
        set "batchArgs=%batchArgs% "%1""
        GOTO QuotesDone
        :NoQuotes
        set "batchArgs=%batchArgs% %1"
    :QuotesDone
    shift
    GOTO ArgLoop
:EndArgLoop

::Create and run the vb script to elevate the batch file
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%temp%\OEgetPrivileges.vbs"
ECHO UAC.ShellExecute "cmd", "/c ""!batchPath! !batchArgs!""", "", "runas", 1 >> "%temp%\OEgetPrivileges.vbs"
"%temp%\OEgetPrivileges.vbs" 
exit /B

:START
::Remove the elevation tag and set the correct working directory
IF '%1'=='ELEV' ( shift /1 )
cd /d %~dp0

::Do your adminy thing here...

Я использую PowerShell, чтобы перезапустить сценарий с повышенными правами, если это не так. Поместите эти строки в самый верх вашего сценария.

net file 1>nul 2>nul && goto :run || powershell -ex unrestricted -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c %~fnx0 %*'"
goto :eof
:run
:: TODO: Put code here that needs elevation

Я скопировал метод 'net name' из ответа @Matt. Его ответ гораздо лучше задокументирован и содержит сообщения об ошибках и тому подобное. Преимущество этого заключается в том, что PowerShell уже установлен и доступен в Windows 7 и более поздних версиях. Нет временных файлов VBScript (*.vbs), и вам не нужно загружать инструменты.

Этот метод должен работать без какой-либо настройки или настройки, если ваши разрешения на выполнение PowerShell не заблокированы.

Для некоторых программ установка супер секрета __COMPAT_LAYER переменная среды для RunAsInvoker будет работать. Проверьте это:

set "__COMPAT_LAYER=RunAsInvoker"
start regedit.exe

Хотя, как это, UAC не будет предлагать пользователю продолжить работу без прав администратора.

Я написал gsudo, аsudoдля окон: повышается в текущей консоли (без переключения контекста в новое окно), с кешем учетных данных (уменьшаются всплывающие окна UAC), а также повышается уровень команд PowerShell.

Это позволяет повышать уровень команд, требующих прав администратора, или всего пакета, если хотите. Просто добавьтеgsudo прежде чем что-либо, что нужно запустить на повышенных уровнях.

Пример командного файла, который поднимается с помощью gsudo:

EDIT: новая версия с одним лайнером, которая работает с любым языком Windows и позволяет избежать проблем с whoami:

net session >nul 2>nul & net session >nul 2>nul || gsudo "%~f0" && exit /b || exit /b
:: This will run as admin ::

Альтернатива (оригинальная версия):

@echo off
  rem Test if current context is already elevated:
  whoami /groups | findstr /b BUILTIN\Administrators | findstr /c:"Enabled group" 1> nul 2>nul && goto :isadministrator
  echo You are not admin. (yet)
  :: Use gsudo to launch this batch file elevated.
  gsudo "%~f0"
  goto end
:isadministrator
  echo You are admin.
  echo (Do admin stuff now).
:end

Установить:

  • через шоколадный: choco install gsudo
  • или совок: scoop install gsudo
  • или возьмите его с github: https://github.com/gerardog/gsudo

Посмотрите на gsudo в действии:gsudo demo

Недавно мне понадобился удобный для пользователя подход, и я придумал его, основываясь на ценной информации, полученной от участников здесь и в других местах . Просто поместите эту строку в начало вашего .bat-скрипта. Обратная связь приветствуется.

      @pushd %~dp0 & fltmc | find "." && (powershell start '%~f0' ' %*' -verb runas 2>nul && exit /b)

Интерпретация:

  • @pushd %~dp0обеспечивает согласованный рабочий каталог относительно этого командного файла; поддерживает пути UNC
  • fltmc собственная команда Windows, которая выводит ошибку, если запускается без повышения
  • | find "." делает ошибку красивее и ничего не выводит при повышении
  • && (если мы успешно получили ошибку, потому что мы не повышены, сделайте это ...
  • powershell startвызвать PowerShell и вызвать командлет Start-Process (start - псевдоним)
  • '%~f0'передать полный путь и имя этого .bat файла. Одиночные кавычки допускают пробелы в пути / имени файла
  • ' %*'передать все аргументы этому .bat файлу. Фанки-кавычки и escape-последовательности, вероятно, не будут работать, но простые строки в кавычках должны. Начальный пробел необходим, чтобы предотвратить поломку, если нет аргументов
  • -verb runasне просто запускайте этот процесс ... Запуск от имени администратора!
  • 2>nul отказаться от неприглядного вывода ошибок PowerShell, если приглашение UAC отменено / игнорируется.
  • если мы успешно запустили себя с помощью PowerShell, то ...
    • ПРИМЕЧАНИЕ: если мы не получаем повышение (пользователь отменяет UAC), тогда && позволяет .bat продолжать работу без повышения прав, так что любые команды, требующие этого, не будут работать, но другие будут работать нормально. Если вы хотите, чтобы сценарий просто завершился, а не запускался без повышения, сделайте это одним амперсандом: &
  • exit /b)выходит из начальной обработки .bat, потому что он нам больше не нужен; у нас есть новый процесс с повышенными привилегиями, в настоящее время использующий .bat. Добавление / b позволяет cmd.exe оставаться открытым, если .bat был запущен из командной строки ... это не имеет никакого эффекта, если .bat был дважды щелкен

Когда сценарию CMD требуются права администратора, и вы это знаете, добавьте эту строку в самый верх сценария (сразу после любого @ECHO OFF):

      NET FILE > NUL 2>&1 || POWERSHELL -ex Unrestricted -Command "Start-Process -Verb RunAs -FilePath '%ComSpec%' -ArgumentList '/c \"%~fnx0\" %*'" && EXIT /b

В NET FILEпроверяет существующие права администратора. Если их нет, PowerShell перезапускает текущий сценарий (с его аргументами) в оболочке с повышенными правами, а сценарий без повышенных прав закрывается.

Если вам не нужно передавать аргументы, то вот компактный скрипт приглашения UAC длиной в одну строку. Это делает то же самое, что и сценарий повышения в ответе, получившем наибольшее количество голосов, но не передает аргументы, поскольку нет надежного способа сделать это, который обрабатывает все возможные комбинации ядовитых символов.

net sess>nul 2>&1||(echo(CreateObject("Shell.Application"^).ShellExecute"%~0",,,"RunAs",1:CreateObject("Scripting.FileSystemObject"^).DeleteFile(wsh.ScriptFullName^)>"%temp%\%~nx0.vbs"&start wscript.exe "%temp%\%~nx0.vbs"&exit)

Поместите это чуть ниже @echo off в вашем пакетном скрипте.

Объяснение

В net sess>nul 2>&1 часть - это то, что проверяет высоту. net sess это просто сокращение для net sessionэто команда, которая возвращает код ошибки, если у сценария нет повышенных прав. Я получил эту идею из этого ТАКОГО ответа. В большинстве ответов здесь используетсяnet file вместо этого, который работает так же.

Затем уровень ошибки проверяется с помощью ||оператор. Если проверка прошла успешно, он создает и выполняет сценарий WScript, который повторно запускает исходный командный файл, но с повышенными правами, прежде чем удалить себя.


Файл WScript - лучший подход, поскольку он быстрый и надежный, хотя он использует временный файл. Вот еще несколько вариантов и их недостатки.

PowerShell

net sess>nul 2>&1||(powershell saps '%0'-Verb RunAs&exit)

Плюсы:

  • Очень короткий.
  • Нет временных файлов.

Минусы:

  • Медленный. PowerShell может запускаться очень медленно.
  • Выдает красный текст, когда пользователь отклоняет приглашение UAC. Команду PowerShell можно обернуть вtry..catch чтобы предотвратить это.

Скрипт мшта WSH

net sess>nul 2>&1||(start mshta.exe vbscript:code(close(Execute("CreateObject(""Shell.Application"").ShellExecute""%~0"",,,""RunAs"",1"^)^)^)&exit)

Плюсы:

  • Быстрый.
  • Нет временных файлов.

Минусы:

  • Ненадежный. Некоторые машины с Windows 10 будут блокировать запуск сценария, поскольку Защитник Windows перехватывает его как потенциального трояна.

Я вставил это в начале сценария:

:: BatchGotAdmin
:-------------------------------------
REM  --> Check for permissions
>nul 2>&1 "%SYSTEMROOT%\system32\icacls.exe" "%SYSTEMROOT%\system32\config\system"

REM --> If error flag set, we do not have admin.
if '%errorlevel%' NEQ '0' (
    echo Requesting administrative privileges...
    goto UACPrompt
) else ( goto gotAdmin )

:UACPrompt
    echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
    echo args = "" >> "%temp%\getadmin.vbs"
    echo For Each strArg in WScript.Arguments >> "%temp%\getadmin.vbs"
    echo args = args ^& strArg ^& " "  >> "%temp%\getadmin.vbs"
    echo Next >> "%temp%\getadmin.vbs"
    echo UAC.ShellExecute "%~s0", args, "", "runas", 1 >> "%temp%\getadmin.vbs"

    "%temp%\getadmin.vbs" %*
    exit /B

:gotAdmin
    if exist "%temp%\getadmin.vbs" ( del "%temp%\getadmin.vbs" )
    pushd "%CD%"
    CD /D "%~dp0"
:--------------------------------------

Используя powershell.

Если файл cmd длинный, я использую первый, который требует повышения, а затем вызываю тот, который выполняет фактическую работу.

Если сценарий представляет собой простую команду, все может поместиться в один cmd файл. Не забудьте указать путь к файлам сценария.

Шаблон:

@echo off
powershell -Command "Start-Process 'cmd' -Verb RunAs -ArgumentList '/c " comands or another script.cmd go here "'"

Пример 1:

@echo off
powershell -Command "Start-Process 'cmd' -Verb RunAs -ArgumentList '/c "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\BIN\x.ps1"'"

Пример 2:

@echo off
powershell -Command "Start-Process 'cmd' -Verb RunAs -ArgumentList '/c "c:\bin\myScript.cmd"'"

Хотя это не относится непосредственно к этому вопросу, потому что он хочет получить некоторую информацию для пользователя, Google привел меня сюда, когда я хотел запустить мой файл.bat с повышенными правами из планировщика задач.

Простейшим подходом было создать ярлык для файла.bat, потому что для ярлыка можно установить Run as administrator прямо из расширенных свойств.

Запустив ярлык из планировщика задач, запустим файл.bat с повышенными правами.

Однострочное пакетное повышение прав пользователя (с аргументами)

Вот моя однострочная версия этого извечного вопроса о пакетном повышении прав пользователей, который актуален и сегодня.
Просто добавьте код в начало вашего пакетного сценария, и все готово.

Тихий

Эта версия ничего не выводит и не приостанавливает выполнение при ошибке.

      @setlocal disabledelayedexpansion enableextensions
@echo off

:: Admin check
fltmc >nul 2>nul || set _=^"set _ELEV=1^& cd /d """%cd%"""^& "%~f0" %* ^"&&((if "%_ELEV%"=="" ((powershell -nop -c start cmd -args '/d/x/s/v:off/r',$env:_ -verb runas >nul 2>nul) || (mshta vbscript:execute^("createobject(""shell.application"").shellexecute(""cmd"",""/d/x/s/v:off/r ""&createobject(""WScript.Shell"").Environment(""PROCESS"")(""_""),,""runas"",1)(window.close)"^) >nul 2>nul)))& exit /b)
Подробный

Подробная версия, которая сообщает пользователю, что запрашиваются права администратора, и приостанавливается при ошибке перед выходом.

      @setlocal disabledelayedexpansion enableextensions
@echo off

:: Admin check
fltmc >nul 2>nul || set _=^"set _ELEV=1^& cd /d """%cd%"""^& "%~f0" %* ^"&&((if "%_ELEV%"=="" (echo Requesting administrator privileges...&((powershell -nop -c start cmd -args '/d/x/s/v:off/r',$env:_ -verb runas >nul 2>nul) || (mshta vbscript:execute^("createobject(""shell.application"").shellexecute(""cmd"",""/d/x/s/v:off/r ""&createobject(""WScript.Shell"").Environment(""PROCESS"")(""_""),,""runas"",1)(window.close)"^) >nul 2>nul))) else (echo This script requires administrator privileges.& pause))& exit /b)

echo Has admin permissions
echo Working dir: "%cd%"
echo Script dir: "%~dp0"
echo Script path: "%~f0"
echo Args: %*

pause

Метод работы

  1. Использует fltmc для проверки прав администратора. (системный компонент, входит в состав Windows 2000+)
  2. Если у пользователя уже есть права администратора, работа продолжается в обычном режиме.
  3. Если нет, порождает свою версию с повышенными правами, используя:
    1. powershell (дополнительная функция Windows, включенная в Windows 7+ по умолчанию, может быть удалена/недоступна в противном случае, может быть установлена ​​в Windows XP/Vista)
    2. mshta (системный компонент, входящий в состав Windows 2000+)
  4. Если не удается получить повышение, останавливает выполнение (вместо бесконечного цикла).

Что отличает это решение от других?

Существуют буквально сотни вариантов решения этой проблемы, но все, что я нашел до сих пор, имеет свои недостатки, и это попытка решить большинство из них.

  • Совместимость. Использование fltmc в качестве средства проверки привилегий, а также powershell или mshta для повышения прав работает с каждой версией Windows, начиная с 2000 года, и должно охватывать большинство конфигураций системы.
  • Никаких лишних файлов не пишет.
  • Сохраняет текущий рабочий каталог. Большинство найденных решений объединяют «каталог сценариев» с «рабочим каталогом», которые являются совершенно разными понятиями. Если вы хотите вместо этого использовать «каталог сценариев», замените с . Некоторые люди выступают за использование вместо этого пути внутри сетевых путей UNC, такие как «\\SOMEONES-PC\share», будут работать, но это также автоматически сопоставит это местоположение с буквой диска (например, Y:), которая может быть или не быть тем, что вы хотите.
  • Останавливается, если не удается набрать высоту. Это может произойти по нескольким причинам, например, нажатие пользователем «Нет» в приглашении UAC, отключение UAC, настройки групповой политики и т. д. Многие другие решения вступают в бесконечный цикл на этом этапе, порождая миллионы командных строк до тех пор, пока не произойдет тепловая смерть Вселенная.
  • Поддерживает (большинство) аргументы командной строки и странные пути. Такие вещи, как амперсанд &, знаки процента %, знаки вставки ^ и несоответствие количества кавычек """'. Вы все еще определенно МОЖЕТЕ сломать это, передав достаточно странные комбинации из них, но это неотъемлемый недостаток пакетной обработки Windows и не может можно обойти, чтобы всегда работать с любой комбинацией.Однако должны быть рассмотрены наиболее типичные варианты использования, а аргументы работают так же, как и без скрипта повышения.

Известные вопросы

Если вы введете аргумент командной строки, который содержит несоответствующее количество двойных кавычек (т. е. не делится на 2), в качестве последнего аргумента будут добавлены дополнительный пробел и символ вставки ^. Например станет . Если это важно для вашего сценария, вы можете добавить логику, чтобы исправить это, например. проверьте, если _ELEV=1 (что означает, что требуется повышение), а затем проверьте, не соответствует ли последний символ списка аргументов ^ и / или количество кавычек, и удалите неправильно работающую каретку.

Пример скрипта для записи вывода в файл

Вы не можете легко использовать > для ведения журнала stdout, потому что при повышении прав создается новое окно cmd и переключается контекст выполнения.

Вы можете добиться этого, передавая все более странные комбинации escape-символов, например но тогда вам нужно будет сделать так, чтобы он всегда порождал новое окно cmd или добавлял логику для удаления кареток, что увеличивает сложность, и во многих сценариях оно все равно будет ломаться.

Лучшим и самым простым способом было бы просто добавить ведение журнала в ваш пакетный скрипт вместо того, чтобы пытаться перенаправить его из командной строки. Это избавит вас от головной боли.

Вот пример того, как вы можете легко реализовать ведение журнала для своего скрипта:

      @setlocal disabledelayedexpansion enableextensions
@echo off

:: Admin check
fltmc >nul 2>nul || set _=^"set _ELEV=1^& cd /d """%cd%"""^& "%~f0" %* ^"&&((if "%_ELEV%"=="" (echo Requesting administrator privileges...&((powershell -nop -c start cmd -args '/d/x/s/v:off/r',$env:_ -verb runas >nul 2>nul) || (mshta vbscript:execute^("createobject(""shell.application"").shellexecute(""cmd"",""/d/x/s/v:off/r ""&createobject(""WScript.Shell"").Environment(""PROCESS"")(""_""),,""runas"",1)(window.close)"^) >nul 2>nul))) else (echo This script requires administrator privileges.& pause))& exit /b)

set _log=
set _args=%*
if not defined _args goto :noargs
set _args=%_args:"=%
set _args=%_args:(=%
set _args=%_args:)=%
for %%A in (%_args%) do (if /i "%%A"=="-log" (set "_log=>> %~n0.log"))
:noargs

if defined _log (echo Logging to file %~n0.log) else (echo Logging to stdout)
echo Has admin permissions %_log%
echo Working dir: "%cd%" %_log%
echo Script dir: "%~dp0" %_log%
echo Script path: "%~f0" %_log%
echo Args: %* %_log%

echo Hello World! %_log%

pause

Бежать: При добавлении аргумента -log выходные данные будут записываться в файл, а не в стандартный вывод.

Заключительные мысли

Меня сбивает с толку, как простая инструкция «ПОВЫШЕНИЕ» не была введена в пакет даже после 15 лет существования UAC. Может быть, однажды Microsoft соберется со своим дерьмом. До тех пор нам приходится прибегать к использованию этих хаков.

      %1 start "" mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c pushd ""%~dp0"" && ""%~s0"" ::","","runas",1)(window.close)&&exit

Попробуй это:

@echo off
CLS
:init
setlocal DisableDelayedExpansion
set cmdInvoke=1
set winSysFolder=System32
set "batchPath=%~0"
for %%k in (%0) do set batchName=%%~nk
set "vbsGetPrivileges=%temp%\OEgetPriv_%batchName%.vbs"
setlocal EnableDelayedExpansion
:checkPrivileges
NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto gotPrivileges ) else ( goto getPrivileges )
:getPrivileges
if '%1'=='ELEV' (echo ELEV & shift /1 & goto gotPrivileges)
ECHO.
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%"
ECHO args = "ELEV " >> "%vbsGetPrivileges%"
ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%"
ECHO args = args ^& strArg ^& " "  >> "%vbsGetPrivileges%"
ECHO Next >> "%vbsGetPrivileges%"
if '%cmdInvoke%'=='1' goto InvokeCmd 
ECHO UAC.ShellExecute "!batchPath!", args, "", "runas", 1 >> "%vbsGetPrivileges%"
goto ExecElevation
:InvokeCmd
ECHO args = "/c """ + "!batchPath!" + """ " + args >> "%vbsGetPrivileges%"
ECHO UAC.ShellExecute "%SystemRoot%\%winSysFolder%\cmd.exe", args, "", "runas", 1 >> "%vbsGetPrivileges%"
:ExecElevation
"%SystemRoot%\%winSysFolder%\WScript.exe" "%vbsGetPrivileges%" %*
exit /B
:gotPrivileges
setlocal & cd /d %~dp0
if '%1'=='ELEV' (del "%vbsGetPrivileges%" 1>nul 2>nul  &  shift /1)
REM Run shell as admin (example) - put here code as you like
ECHO %batchName% Arguments: P1=%1 P2=%2 P3=%3 P4=%4 P5=%5 P6=%6 P7=%7 P8=%8 P9=%9
cmd /k

Если вам нужна информация об этом командном файле, запустите фрагмент HTML/JS/CSS:

document.getElementsByTagName("data")[0].innerHTML="ElevateBatch, version 4, release<br>Required Commands:<ul><li>CLS</li><li>SETLOCAL</li><li>SET</li><li>FOR</li><li>NET</li><li>IF</li><li>ECHO</li><li>GOTO</li><li>EXIT</li><li>DEL</li></ul>It auto-elevates the system and if the user presses No, it just doesn't do anything.<br>This CANNOT be used to create an Elevated Explorer.";
data{font-family:arial;text-decoration:none}
<data></data>

Следующее решение является чистым и работает отлично.

  1. Загрузите почтовый файл Elevate по https://www.winability.com/download/Elevate.zip

  2. Внутри zip вы должны найти два файла: Elevate.exe и Elevate64.exe. (Последняя представляет собой встроенную 64-разрядную компиляцию, если вам требуется, хотя обычная 32-разрядная версия Elevate.exe должна нормально работать как с 32-, так и с 64-разрядными версиями Windows)

  3. Скопируйте файл Elevate.exe в папку, где Windows всегда может его найти (например, C:/Windows). Или вы можете лучше скопировать в ту же папку, где вы планируете сохранить файл bat.

  4. Чтобы использовать его в командном файле, просто добавьте команду, которую вы хотите выполнить от имени администратора, с помощью команды elevate, например:

 elevate net start service ...
Другие вопросы по тегам