Как передать аргументы командной строки в файл ps1 PowerShell

90

В течение многих лет я использовал cmd/DOS/Windowsоболочку и передавал аргументы командной строки пакетным файлам. Например, у меня есть файл, zuzu.batи в нем я доступ %1, %2и т.д. Теперь я хочу сделать то же самое , когда я называю PowerShellсценарий when I am in a Cmd.exe shell. У меня есть сценарий xuxu.ps1(и я добавил PS1 в свою переменную PATHEXT и связанные файлы PS1 с PowerShell). Но что бы я ни делал, мне кажется, что я ничего не могу получить от $argsпеременной. Он всегда имеет длину 0.

Если я нахожусь в PowerShellоболочке cmd.exe, это работает (конечно). Но мне еще недостаточно комфортно жить в среде PowerShell на постоянной основе. Я не хочу печатать powershell.exe -command xuxu.ps1 p1 p2 p3 p4. Я хочу печатать xuxu p1 p2 p3 p4.

Возможно ли это, и если да, то как?

Образец, который я не могу заставить работать, является тривиальным, foo.ps1:

Write-Host "Num Args:" $args.Length;
foreach ($arg in $args) {
    Write-Host "Arg: $arg";
}

Результат всегда такой:

C:\temp> foo
Num Args: 0
C:\temp> foo a b c d
Num Args: 0
c:\temp>
Дэниел 'Dang' Гриффит
источник

Ответы:

34

Эта статья помогает. В частности, этот раздел:

-Файл

Запускает указанный сценарий в локальной области («с точечным источником»), так что функции и переменные, создаваемые сценарием, доступны в текущем сеансе. Введите путь к файлу сценария и любые параметры. Файл должен быть последним параметром в команде, поскольку все символы, вводимые после имени параметра файла, интерпретируются как путь к файлу сценария, за которым следуют параметры сценария.

т.е.

powershell.exe -File "C:\myfile.ps1" arg1 arg2 arg3

означает запуск файла myfile.ps1, а arg1 arg2 и arg3 - это параметры для сценария PowerShell.

Arj
источник
1
Это по-прежнему не помогает с тем, что хочет оператор («Я хочу напечатать xuxu p1 p2 p3 p4»).
thdoan
19

Покопавшись в документации PowerShell, я обнаружил полезную информацию об этой проблеме. Вы не можете использовать, $argsесли вы использовали param(...)в начале вашего файла; вместо этого вам нужно будет использовать $PSBoundParameters. Я скопировал / вставил ваш код в сценарий PowerShell, и он работал, как и следовало ожидать, в PowerShell версии 2 (я не уверен, в какой версии вы были, когда столкнулись с этой проблемой).

Если вы используете $PSBoundParameters(и это работает ТОЛЬКО, если вы используете param(...)в начале своего скрипта), то это не массив, это хеш-таблица, поэтому вам нужно будет ссылаться на нее, используя пару ключ / значение.

param($p1, $p2, $p3, $p4)
$Script:args=""
write-host "Num Args: " $PSBoundParameters.Keys.Count
foreach ($key in $PSBoundParameters.keys) {
    $Script:args+= "`$$key=" + $PSBoundParameters["$key"] + "  "
}
write-host $Script:args

А когда звонили с ...

PS> ./foo.ps1 a b c d

В результате ...

Num Args:  4
$p1=a  $p2=b  $p3=c  $p4=d
Рэндалл Борк
источник
Это не учитывает OP, запускающего свою командную строку с помощью powershell.exe или pwsh. Поведение меняется, когда OP делает это.
Эрик Хансен
1
@EricHansen Я не знаю, что вы имеете в виду, я получаю тот же результат в любом случае: `Powershell> powershell.exe. \ ParamTest.ps1 val1 val2 val3 val4 Num Args: 4 $ p1 = val1 $ p2 = val2 $ p3 = val3 $ p4 = val4 `
Randall Borck
@RandallBrock Поведение аргументов для меня меняется. Если я использую CMD / batch и делаю что-то подобное pwsh .\ParamTest.ps1 -arg1 val1 -listOfArgs val2 val3 val4, мне это действительно не нравится. С другой стороны, если я использую PowerShell и работаю .\ParamTest.ps1 -arg1 val1 -listOfArgs val2 val3 val4, это работает так, как я ожидал. Я слышал, что именно так он и должен работать из «соображений безопасности».
Эрик Хансен
@EricHansen Интересно, дело ли в версии. Для меня pwsh запускает 6.2.0, но powershell.exe запускает 5.1.17134.858, обе из которых дают одинаковые перечисленные результаты: Powershell>pwsh .\ParamTest.ps1 val1 val2 val3 val4дает:Num Args: 4 $p1=val1 $p2=val2 $p3=val3 $p4=val4
Randall Borck
1
@Timo Я не знаю, что именно вы делаете, но paramэто языковая конструкция, но она должна быть первой в файле. Вы перед этим объявили переменную или что-то еще? Дополнительная информация здесь: docs.microsoft.com/en-us/powershell/module/…
Рэндалл Борк,
18

Хорошо, сначала это нарушает базовую функцию безопасности в PowerShell. При таком понимании вот как вы можете это сделать:

  1. Откройте проводник Windows. окно
  2. Инструменты меню -> Параметры папки -> вкладка Типы файлов
  3. Найдите тип файла PS1 и нажмите кнопку «Дополнительно».
  4. Нажмите кнопку New
  5. Для Действия поставить: Открыть
  6. Для Приложения укажите: "C: \ WINNT \ system32 \ WindowsPowerShell \ v1.0 \ powershell.exe" "-file" "% 1"% *

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

EBGreen
источник
4
Я думаю, что ключ находится на вашем шаге 6, где вы передаете параметры в powershell.exe. Дэниел говорит, что он связал файлы PS1 с PowerShell, но он не передает аргументы без дополнительных спецификаций% 1% *. Также обратите внимание, что параметр -File недоступен в V1. Это новость для V2.
Кейт Хилл
Хорошая уловка относительно параметра -file. Я забыл об этом.
EBGreen
Мне придется установить V2, прежде чем я смогу попробовать ваше предложение. Благодарю. Когда вы говорите, что это нарушает базовую функцию безопасности, что вы имеете в виду под словом «это»? Вызов сценария PowerShell из Cmd.exe, как если бы это был файл .com / .bat / .exe? Передавать параметры скрипту?
Дэниэл 'Dang' Гриффит
1
Извините, я должен был быть более ясным. Вызов скрипта без явного вызова powershell.exe. Я не говорю, что это важная функция безопасности для вас лично, и это безопасность через неизвестность, которую я не всегда фанат.
EBGreen
6
Чтобы добавить к комментарию EBGreen, основная проблема безопасности, которую PowerShell пытается избежать, - это двойной щелчок по файлам PS1, прикрепленный к электронной почте, и запуск сценария. Вот почему файлы PS1 по умолчанию связаны только с редактором. Microsoft действительно не хочет версию вируса ILoveYou для PowerShell, например «LOVE-LETTER-FOR-YOU.TXT.ps1»
Кейт Хилл,
12

Вы можете объявить свои параметры в файле, например param:

[string]$para1
[string]$param2

А затем вызовите файл PowerShell вот так .\temp.ps1 para1 para2....para10и т. Д.

VB
источник
6

Возможно, вы можете заключить вызов PowerShell в .batфайл следующим образом:

rem ps.bat
@echo off
powershell.exe -command "%*"

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

ps foo 1 2 3

Однако цитирование может быть немного запутанным:

ps write-host """hello from cmd!""" -foregroundcolor green
Guillermooo
источник
+1 за отображение тройных кавычек. это заставило меня застрять на какое-то время.
DeanOC
1

Вы не можете получить "xuxu p1 p2 p3 p4", как кажется. Но когда вы находитесь в PowerShell и устанавливаете

PS > set-executionpolicy Unrestricted -scope currentuser

Вы можете запускать эти сценарии следующим образом:

./xuxu p1 p2 p3 p4

или же

.\xuxu p1 p2 p3 p4

или же

./xuxu.ps1 p1 p2 p3 p4

Надеюсь, вам станет удобнее работать с PowerShell.

Атик Рахман
источник
0

если вы хотите вызывать сценарии ps1 из cmd и передавать аргументы, не вызывая сценарий, например

powershell.exe script.ps1 -c test
script -c test ( wont work )

вы можете сделать следующее

setx PATHEXT "%PATHEXT%;.PS1;" /m
assoc .ps1=Microsoft.PowerShellScript.1
ftype Microsoft.PowerShellScript.1=powershell.exe "%1" %*

Предполагается, что powershell.exe находится на вашем пути

https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/ftype

ta32
источник
Стоит отметить, что для этого требуется запустить командную строку от имени администратора. Кроме того, я изо всех сил пытаюсь заставить его работать в Windows 10 версии 1903 (18362.778) - все команды выполняются успешно, но аргументы по-прежнему не передаются. Я думаю, что упаковка с помощью файла .bat является наиболее переносимым решением.
Давид Айрапетян