Для удобства использования мне нужно поместить свой powershell-скрипт в bat-файл. Содержимое bat-файла следующее:
# 2>NUL & @CLS & PUSHD "%~dp0" & powershell -COMMAND "Start-Process powershell -Verb RunAs -ArgumentList '-windowstyle hidden -nol -nop -ep bypass ""[IO.File]::ReadAllText(''%~f0'')|iex""'"
# powershell script
Однако в сценарии PowerShell мне нужно получить путь к bat-файлу. Я знаю, что могу получить полный путь с помощью 'SET "batFilePath=%~f0"'... но я не знаю, что мне следует делать дальше, чтобы иметь возможность передать и использовать аргумент batFilePath в сценарии powershell. Пожалуйста, помогите мне, большое спасибо.
P/s: Дело не в том, что я ленивый, я пробовал искать похожие темы, но ответы меня все очень сбивают с толку, не знаю, что делать в моем случае. Прошу прощения.
У вас есть аргументы внутри одинарных кавычек вместо двойных.
@MathiasR.Jessen Я создал небольшое программное обеспечение и хочу поделиться им с друзьями, которые не знают, как использовать код. Я хочу, чтобы для них все было максимально просто: просто кликни и беги. Я пытался упаковать exe-файл с помощью PS2EXE, но AV всегда получал ложные срабатывания, поэтому я решил создать bat-файл. Если вам интересно, я опубликую исходный код на GitHub и надеюсь, что вы дадите мне отзыв. Спасибо.
Если вы запустите «SET «batFilePath=%~f0»» в cmd, вы можете получить доступ к переменной в PowerShell, запустив «$env:batFilePath».
Вот пример кода
# 2>NUL & @CLS & SET "batFilePath=%~f0" & powershell -Command "Start-Process powershell -Verb RunAs -ArgumentList ""-windowstyle hidden -nol -nop Invoke-Expression (Get-Content -Path $env:batFilePath -Raw)"""
Обновлять: Чтобы передать $batFilePath в текущий процесс PowerShell, вы можете использовать этот код
# 2>NUL & @CLS & SET "batFilePath=%~f0" & powershell -Command "Start-Process powershell -Verb RunAs -ArgumentList \"-NoLogo -NoProfile -Command `$batFilePath='$env:batFilePath'; Invoke-Expression (Get-Content -Path $env:batFilePath -Raw)\"" & exit
На самом деле вам не нужно устанавливать «batFilePath» в cmd, вы можете просто использовать «~f0» вот так
# 2>NUL & @CLS & powershell -Command "Start-Process powershell -Verb RunAs -ArgumentList \"-NoLogo -NoProfile -Command `$batFilePath='%~f0'; Invoke-Expression (Get-Content -Path %~f0 -Raw)\"" & exit
Большое спасибо. Но я хочу использовать $batFilePath внутри сценария PowerShell, например, для регистрации запуска Windows «New-ItemProperty -Path $regKeyPath -Name $regKeyName -Value $batFilePath -PropertyType String -Force». Итак, как мне передать аргумент $batFilePath? Пожалуйста, просто помогите мне.
@NqHai, что ты не понимаешь в переменных окружающей среды? Доступ к переменным среды осуществляется так же, как показано в примере: $env:batFilePath
@Squashman Да, мои знания очень ограничены. Я попробовал протестировать код, используя содержимое bat-файла, следующим образом: #2>NUL... (удалить -windowstyle скрытый и добавить -noexit). 2-я строка: Write-Host «Hello $env:batFilePath». Но я получаю только «Привет». Я не знаю способа передать в скрипт $env:batFilePath.
@yy Большое спасибо за терпеливое руководство, я понял на вашем примере. Хорошего дня!
Ваша задача сложна, поскольку она требует передачи значения процессу, который запущен с повышенными правами (запускается с правами администратора), используя
‼Старт-Процесс -Verb RunAs
.
В этом случае вы не можете использовать переменные среды, поскольку процесс с повышенными правами (в целях безопасности) не наследует переменные среды вызывающего объекта:[1]
Поэтому вам придется «встроить» интересующие значения в (подразумеваемый) -Command
аргумент, который передается экземпляру powershell.exe с повышенными правами, что может оказаться затруднительным с точки зрения цитирования и экранирования.
Кроме того, в Windows PowerShell процесс с повышенными правами не наследует рабочий каталог вызывающего объекта и по умолчанию имеет значение C:\Windows\System32
; к счастью, эта проблема исправлена в PowerShell (Core) 7.
Ниже приведена модифицированная версия гибридного пакетного файла, которая наглядно демонстрирует определение переменной (только для оболочки) $batFilePath
с полным путем к исходному пакетному файлу (%~f0
), а также сначала переход к рабочему каталогу вызывающего объекта (%~dp0
) с помощью Установить-Местоположение:
<# ::
@echo off & setlocal
powershell -NoProfile Start-Process powershell -Verb RunAs -ArgumentList '-NoExit -WindowStyle Normal -NoProfile -ExecutionPolicy Bypass Set-Location \\\"%~dp0\\"; $batFilePath = \\\"%~f0\\\"; Invoke-Expression (Get-Content -Raw \\\"%~f0\\\")'
exit /b
#>
# PowerShell code goes below; it is executed in the context of an
# elevated PowerShell session based on the `powershell` call above.
[pscustomobject] @{
batFilePath = $batFilePath
workingDir = $PWD
}
То есть, приведенное выше создает видимый и открытый экземпляр PowerShell с повышенными правами, который демонстрирует, что (а) рабочий каталог совпадает с каталогом вызывающего объекта и (б) эта переменная $batFilePath
отражает полный путь к исходному пакетному файлу.
Примечание:
%~f0
и %~dp0
используются для обозначения полного пути к файлу вызывающего пакетного файла и полного пути к каталогу, в котором он находится, с использованием синтаксиса расширения аргументов cmd.exe
, как описано в выводе cmd /c call /?
Интерфейс командной строки PowerShell с -Command
/-c
, который является подразумеваемым параметром в CLI Windows PowerShell, powershell.exe
, требует символов "
. экранироваться как \"
, чтобы дословно передаваться как часть кода.
-File
и требует явного использования -Command
/ -c
для передачи исходного кода PowerShell в качестве аргумента. Здесь применяются те же правила экранирования, хотя pwsh.exe
в Windows также принимает ""
как экранированный "
, что на самом деле предпочтительнее при вызове из cmd.exe
(пакетных файлов), чтобы избежать нарушения синтаксиса cmd.exe
.Поскольку используется вложенный вызов powershell.exe
, в приведенном выше примере используется \\\"
для экранирования символов "
. чтобы в конечном итоге перейти к вложенному экземпляру с повышенными правами powershell.exe
. Однако, поскольку %~dp0
всегда расширяется до пути к каталогу с завершающим \
, за ним следует только \\"
выше.
[1] By contrast, a directly launched PowerShell instance implicitly inherits all of the caller's environment variables, and since using the SET
statement in a batch file to define a variable invariably creates an environment variable, such a PowerShell instance automatically sees such variables, using $env:
namespace variable notation - see the conceptual about_Environment_Variables help topic.
E.g., calling SET FOO=bar
in the calling batch file before invoking the PowerShell CLI allows the latter to reference said variable('s value) as $env:FOO
.
Отлично, ваш ответ помог мне понять гораздо больше. Мне еще предстоит многому научиться. Искренне спасибо!
Потому что я попробовал использовать строку кода yy. Результаты, которые я получил, были именно теми, которые мне были нужны, поэтому я думаю, что это подходящий и самый ранний ответ в данном случае. Я не знаю достаточно, чтобы прокомментировать, какой ответ лучше, поэтому я просто выбираю ответ, который могу понять, и сразу же практикую. Несмотря на это, ваш ответ очень проницателен, и я буду тратить больше времени на обучение у вас. Большое спасибо.
Рад это слышать, @NqHai, и ценю обратную связь. Кроме того, я ошибся в ответе yy - последняя команда устанавливает переменную, как и было запрошено.
Похоже, было бы гораздо удобнее не использовать bat-файл. Какова настоящая причина? Какая проблема возникает, если вы не сохраняете сценарий PowerShell в пакетном файле?