使用cmd.exe执行多行PowerShell命令

3

我想在cmd.exe的.bat脚本中指定一个多行PowerShell命令。 我显然还没有正确的换行符和/或引号。 我也尝试使用反引号作为换行符,但同样失败了。 请问如何正确输入这个命令?

PS C:\src\t> cat .\pd.bat
powershell -NoProfile -Command "Get-ChildItem -Path '../' -Filter '*.doc' | ^
    Select-Object -First 1 | ^
    ForEach-Object { notepad `"$_.FullName`" }"
PS C:\src\t> .\pd.bat

C:\src\t>powershell -NoProfile -Command "Get-ChildItem -Path '../' -Filter '*.doc' | ^
^ : The term '^' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if
a path was included, verify that the path is correct and try again.
At line:1 char:45
+ Get-ChildItem -Path '../' -Filter '*.doc' | ^
+                                             ~
    + CategoryInfo          : ObjectNotFound: (^:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

2
为什么要首先使用cmd.exe?直接从PowerShell提示符中键入所需的命令即可。 - Bill_Stewart
1
特别是当 cmd.exe 唯一的优势是内存占用更小(比 PowerShell 小几个数量级)时,如果你要启动 PowerShell,就直接使用 PowerShell。 - David C. Rankin
2
我想学习将PowerShell嵌入.bat脚本所需的机制,因为有些人永远不会放弃cmd shell。这也意味着让人们使用他们熟悉的任何东西。如果他们看到PowerShell更强大和可读,也许他们会开始使用更多的PowerShell命令。这是一个漫长的过程,不是一夜之间的变化。 - lit
4个回答

8

除了使用中间临时文件外,您可以使用^ - 转义:

powershell -NoProfile -Command Get-ChildItem -Path '../' -Filter '*.doc' ^| ^
Select-Object -First 1 ^| ^
ForEach-Object { notepad "\"$($_.FullName)\"" }

请注意,在PowerShell中,notepad "$_.FullName"是无法正常工作的,因为你需要一个封闭的$(...)来引用属性"..."中。我已经纠正了上面的问题,但请注意,在这种情况下根本不需要双引号。
必须使用^
  • 转义|以防止cmd.exe提前解释。

    • 通常,如果要将它们传递到PowerShell,则必须^-转义cmd.exe的所有元字符:& | < > %

    • 您希望PowerShell文字直接看到的"实例(当它们作为PowerShell源代码解释时具有语法意义)是一种特殊情况:您必须\-转义它们(!)并且"..."-包含您希望被PowerShell识别为单个参数的字符串,以便准确保留嵌入式空格

    • 注意:您不能在整个-Command参数周围使用"..."引用,因为cmd.exe不支持多行字符串文字("..."中的^被解释为字面值),即使是逐行的"..."引用也不能按预期工作。

  • 转义换行符以告诉cmd.exe命令在下一行继续。

    • 请注意,^必须是该行的最后一个字符。
    • 还要注意,行结束的^删除换行符,因此生成的命令是单行命令。

要在命令中包含实际的换行符-这对于像Python这样的语言传递命令是必需的,如eryksun指出-请使用^<newline><newline>,如PetSerAl建议:

powershell.exe -noprofile -command "\"line 1^

line 2\""

上述结果是基于 PowerShell 简单地“回显”(输出)一个作为命令提交的带引号字符串文字而得出的:
line 1
line 2

注意:"\"(以及匹配的\"")不仅在此处最终正确保留带有嵌入式空格的字符串时是必需的,而且为了向cmd.exe呈现一组平衡的"(与\转义无关,cmd.exe无法识别),这也是必需的-如果没有它,则该行末尾的换行符转义^将无法被识别。
PetSerAl还指出,如果正在传递PowerShell应视为字符串字面量的内容,则还可以传递PowerShell最终看到的单引号字符串('...')。
powershell.exe -noprofile -command ^"'line 1^

line 2'^"

在这里,对"进行的^转义是为了使cmd.exe不会将换行符转义的^误认为在双引号字符串中。

我发现在PowerShell中没有使用子表达式是因为我没有对其进行引用。{ notepad $_.FullName }可以工作,而{ notepad "$_.FullName" }会失败。在引号内使用子表达式可以产生期望的结果。这似乎很奇怪。 - lit
可能会让人感到惊讶,但这就是PowerShell的工作原理 - 参见我的这个答案 - mklement0
我会将这篇文章加入书签,以便将来用作重复闭包。 - Gerhard

2
我支持Bill_Stewart的评论,在本机PowerShell控制台中完成即可,无需使用cmd。
如果您需要在cmd中完成,请在每行前使用引号,然后再使用^。并且在ForEach-Object块内删除反引号。
powershell -NoProfile -Command "Get-ChildItem -Path '../' -Filter '*.doc' |" ^
"Select-Object -First 1 ^| ^
"ForEach-Object { notepad "$_.FullName" }

1

通常情况下,我会尽可能将命令压缩成一行。您可以通过像JavaScript或C#一样使用分号来终止命令来实现这一点。在您的情况下,我甚至不认为需要使用终止符。您可以将其合并到一行中:

powershell -NoProfile -Command "Get-ChildItem -Path '../' -Filter '*.doc' |     Select-Object -First 1 | ForEach-Object { notepad `"$_.FullName`" }"

另一个选择是将您的PowerShell保存到外部文件中并进行调用:

powershell.exe -File MyPSScript.ps1


1

pd.bat的内容必须如下:

powershell -NoProfile -Command Get-ChildItem -Path '../' -Filter '*.doc' ^| ^
    Select-Object -First 1 ^| ^
    ForEach-Object { notepad $_.FullName }

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