如何将命令行参数传递给PowerShell ps1文件

114
多年来,我一直在使用 cmd/DOS/Windows shell,并将命令行参数传递给批处理文件。例如,我有一个文件,zuzu.bat,其中我访问 %1%2 等。现在,当我在 Cmd.exe shell 中调用 PowerShell 脚本时,我想要做同样的事情。我有一个脚本,xuxu.ps1(我已将 PS1 添加到我的 PATHEXT 变量中,并将 PS1 文件与 PowerShell 相关联)。但是,无论我做什么,似乎都无法从 $args 变量中获取任何内容。它总是长度为 0。
如果我在 PowerShell shell 中,而不是 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>
8个回答

51

这篇文章有所帮助,尤其是这个部分:

-File

在本地范围内(“点源”)运行指定的脚本,以便脚本创建的函数和变量在当前会话中可用。输入脚本文件路径和任何参数。File必须是命令中的最后一个参数,因为在File参数名称后键入的所有字符都被解释为脚本文件路径,然后是脚本参数。

即:

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

运行myfile.ps1文件,并使用arg1 arg2 & arg3作为PowerShell脚本的参数。


3
这仍然无法满足提问者的需求(“我想输入 xuxu p1 p2 p3 p4”)。 - thdoan
但是空格怎么办?如果 arg1 是一个带有空格的路径。 - Andrei Krasutski
@AndreiKrasutski 转义机制取决于脚本的执行方式。由于它表示为 "powershell.exe",我会假设这将在 CMD.EXE(Windows) shell 中运行。在这种情况下,您可以在路径周围添加引号,就像 "C:\myfile.ps1" 被转义一样。按照同样的示例:powershell.exe -File "C:\myfile.ps1" "arg1a arg1b" arg2 arg3将返回:Num Args: 3 Arg: arg1a arg1b \r\n Arg: arg2 \r\n Arg: arg3 (我不能在评论中放置新行) - Ben Keene
很遗憾,当参数包含空格和尾部反斜杠时,-File命令会出现问题。例如:powershell.exe -file "C:\myfile.ps1" "C:\F 1\" "C:\F 2\"会返回:C:\F 1" C:\F \r\n 2" - undefined

26

通过查阅PowerShell文档,我发现了关于这个问题的一些有用信息。如果您在文件开头使用了 param(...),则无法使用 $args;相反,您需要使用 $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 这样做时,行为会发生变化。 - Eric Hansen
2
@EricHansen 我不知道你的意思,无论哪种方式我都得到相同的结果: Powershell>powershell.exe .\ParamTest.ps1 val1 val2 val3 val4 参数数量: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,那就像我期望的那样工作。我听说这是由于“安全原因”而设计成这样的。 - Eric Hansen
我得到了以下信息:param : Die Benennung "param" wurde nicht als Name eines Cmdlet, einer Funktion, einer Skriptdatei oder eines ausführbaren Programms erkannt. 我在 PowerShell 终端上调用它的方式是:greatstuff.ps1 argu1 - Timo
1
@Timo 我不知道你具体在做什么,但是 param 是一种语言结构,必须放在文件的第一行。你是否在它之前声明了一个变量或其他内容?更多信息请参见:https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters?view=powershell-6 - Randall Borck
显示剩余4条评论

19

好的,首先这会破坏PowerShell的一个基本安全功能。有了这个理解,下面是你可以执行的操作:

  1. 打开一个Windows Explorer窗口
  2. 菜单 工具 -> 文件夹选项 -> 选项卡 文件类型
  3. 找到PS1文件类型并点击高级按钮
  4. 点击新建按钮
  5. 在操作中输入: 打开
  6. 在应用程序中输入: "C:\WINNT\system32\WindowsPowerShell\v1.0\powershell.exe" "-file" "%1" %*

您可能还想在其中加入-NoProfile参数,这取决于您的配置文件执行什么操作。


4
我认为关键在于你的步骤6,也就是将参数传递给powershell.exe。 丹尼尔说他已经将PS1文件与PowerShell相关联,但是如果没有额外的%1 %*规范,参数就无法传递。此外,请注意“-File”参数在V1中不可用,它是V2中新增的。 - Keith Hill
关于-file参数的提醒很好,我忘记了。 - EBGreen
1
抱歉,我应该表述得更清楚。是指在不显式调用powershell.exe的情况下调用脚本。我并不是说这对你个人来说是一个重要的安全功能,而且这种安全性是通过混淆实现的,我也并不总是支持这种方法。 - EBGreen
6
补充EBGreen的评论,PowerShell试图避免的基本安全问题是人们双击附加在电子邮件中的PS1文件并运行脚本。这就是为什么PS1文件默认仅与编辑器相关联。微软真的不希望出现PowerShell版本的ILoveYou病毒,例如"LOVE-LETTER-FOR-YOU.TXT.ps1"。 - Keith Hill
"%1" %* 的意思是什么? - Teoman shipahi
显示剩余3条评论

15
你可以在文件中声明参数,例如param:
[string]$param1
[string]$param2

然后像这样调用PowerShell文件 .\temp.ps1 param1 param2....param10,等等。


6
也许您可以像这样在一个.bat文件中调用PowerShell:
rem ps.bat
@echo off
powershell.exe -command "%*"

如果您将此文件放置在PATH中的一个文件夹下,那么您可以像这样调用PowerShell脚本:
ps foo 1 2 3

引用可能会有点混乱:

ps write-host """hello from cmd!""" -foregroundcolor green

+1 是为了展示三引号。这让我卡了一段时间。 - DeanOC
仅为完整性而言 - 关于引用(和传递制表符和互操作性); 如果您有一个名为“SetTabEV.cmd”的命令提示脚本,用于将名为“Tab”的环境变量设置为/具有制表符字符的值 - 'Set Tab =',运行脚本,然后您也可以使用它与4个引号;例如从命令提示符:SetTab.cmd powershell MyScript.ps1 -dol """"1%Tab%Dennis%Tab%2047""""但是,如果您使用/传递PowerShell制表符字符转义序列,则只需要3个/三重引号即可:powershell MyScript.ps1 -dol """1`tDennis`t2047""" - DennisVM-D2i

2

如果你想要在命令行中调用ps1脚本并传递参数,而不需要直接调用脚本,可以这样做:

Original Answer翻译成"最初的回答"

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://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/ftype "Original Answer"翻译成"最初的回答"。

值得注意的是,这需要以管理员身份运行cmd提示符。此外,我正在努力使其在Windows 10版本1903(18362.778)上实际工作 - 所有命令都成功运行,但参数仍未传递。我认为使用.bat文件包装是最便携的解决方案。 - David Airapetyan

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
我想要自动化在Windows中解除对从互联网下载的文件的“阻止”,所以我让我的.ps1文件工作了,但是当我想要将其作为“右键单击”菜单选项时,尽管尝试了所有可能的组合(仅传递参数给cmd,然后传递给powershell或ps1,使用单引号/双引号等),它从未起作用!
经过几个小时的努力,我终于成功通过在同一命令中同时使用cmd和powershell来使其工作,如下所示:
cmd.exe /c start powershell "C:\folder\script.ps1 -folder '%1'"
其中,folder是指右键单击的文件夹的参数。如果有人感兴趣,这是ps1文件的内容:
param (
    [string]$folder = "$env:userprofile\Downloads"
    #defaults to the current user Downloads folder
)
# Remove Recurse if you don't want to unblock inner folders
get-childitem "$folder" -Recurse | unblock-file

你可以制作一个.reg文件,将其作为右键选项添加到一次性解除所有文件的功能。
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\shell\UnblockDL]
@="Unblock DL Files"

[HKEY_CLASSES_ROOT\Directory\shell\UnblockDL\command]
@="cmd.exe /c start powershell \"C:\\folder\\script.ps1 -folder '%1'\""

记得将"C:\folder\script.ps1"调整为你自己的ps1文件,并确保其名称/路径不包含任何空格(别问我为什么!)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接