如何使 ($Line_1`n$Line_2) 在 CMD 脚本中工作?
How to make ($Line_1`n$Line_2) work in a CMD script?
我在CMD脚本文件中使用了以下代码
PowerShell Add-Type -AssemblyName System.Windows.Forms;^
$Line_1 = 'Hello!';^
$Line_2 = 'How are you?';^
[System.Windows.Forms.MessageBox]::Show($Line_1)
以上只会显示($Line_1)
如果使用 ($Line_1`n$Line_2)
,则不会显示任何内容。
如何让它同时显示 $Line_1
和 $Line_2
?
您可以查找多种连接字符串的方法,但最简单的方法可能是使用 + 符号。 `n 是一个换行符,它将第 2 行放在第 1 行下方。请注意,` 是一个反引号(通常与波浪号位于同一键上 ~)
[System.Windows.Forms.MessageBox]::Show($Line_1 + "`n" + $Line_2)
再次查看您的 post 后,我发现您与 ($Line_1`n$Line_2) 很接近。你只是缺少一些双引号
[System.Windows.Forms.MessageBox]::Show("$Line_1`n$Line_2")
Powershell 乐于用双引号内的值替换变量。您可以阅读有关可扩展字符串的更多信息 and here
显然$Line_1 + "`n" + $Line_2
和"$Line_1`n$Line_2"
正常工作。将命令字符串从 cmd 发送到具有其遗留怪癖的 PowerShell 只是很棘手,因为:
- 在cmd中
()
是各个地方的特殊字符,表示一个块
- 令牌分隔符不仅是
<space>
和<tab>
,还有;
,
=
<0x0B>
<0x0C>
and <0xFF>
。这会更改 cmd 的标记化行为,但被调用的命令可能会再次使用其规则重新解析命令
根据文档,PowerShell 期望在最后一个参数 中使用 单个字符串 中的命令(这并不完全正确,因为文档未正确更新),因此您需要引用整个内容 或转义所有分隔符。最简单的解决方案是使用单行并像这样
转义 "`n"
中的引号
PowerShell "Add-Type -AssemblyName System.Windows.Forms; $Line_1 = 'Hello!'; $Line_2 = 'How are you?'; [System.Windows.Forms.MessageBox]::Show($Line_1 + \"`n\" + $Line_2)"
如果你想将命令放在多行中,那么你不能引用字符串。现在要将整个内容作为一个参数,您需要转义所有空格(在这种情况下,您不需要以某种方式转义 ;
,可能是因为在将命令行传递给 PowerShell 后,它会调用 GetCommandLineW
并再次解析整个事情本身)
PowerShell Add-Type^ -AssemblyName^ System.Windows.Forms;^
$Line_1^ =^ 'Hello!';^
$Line_2^ =^ 'How^ are^ you?';^
[Windows.Forms.MessageBox]::Show($Line_1^ +^ \"`n\"^ +^ $Line_2)"
或者,您可以通过直接使用 [char]10
获取新行来避免 "`n"
字符串
PowerShell -Command Add-Type -AssemblyName System.Windows.Forms;^
$Line_1 = 'Hello!';^
$Line_2 = 'How are you?';^
[System.Windows.Forms.MessageBox]::Show($Line_1 + [char]10 + $Line_2)
最后一个无需任何转义即可工作的解决方案利用 PowerShell 的 EncodedCommand
选项接收 UTF-16 命令字符串的 base64 编码字符串。您可以通过 运行 在 PowerShell
中获取编码版本
$str = @'
>> Add-Type -AssemblyName System.Windows.Forms;
>> $Line_1 = 'Hello!';
>> $Line_2 = 'How are you?';
>> [System.Windows.Forms.MessageBox]::Show($Line_1 + "`n" + $Line_2)
>> '@
PS C:\Users\phucl> [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($str))
QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7AAoAIAA9ACAAJwBIAGUAbABsAG8AIQAnADsACgAgAD0AIAAnAEgAbwB3ACAAYQByAGUAIAB5AG8AdQA/ACcAOwAKAFsAUwB5AHMAdABlAG0ALgBXAGkAbgBkAG8AdwBzAC4ARgBvAHIAbQBzAC4ATQBlAHMAcwBhAGcAZQBCAG8AeABdADoAOgBTAGgAbwB3ACgAIAArACAAIgAKACIAIAArACAAKQA=
获得编码版本后,您可以从 cmd 调用它
PowerShell -EncodedCommand QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7AAoAJABMAGkAbgBlAF8AMQAgAD0AIAAnAEgAZQBsAGwAbwAhACcAOwAKACQATABpAG4AZQBfADIAIAA9ACAAJwBIAG8AdwAgAGEAcgBlACAAeQBvAHUAPwAnADsACgBbAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwAuAE0AZQBzAHMAYQBnAGUAQgBvAHgAXQA6ADoAUwBoAG8AdwAoACQATABpAG4AZQBfADEAIAArACAAIgBgAG4AIgAgACsAIAAkAEwAaQBuAGUAXwAyACkA
最简单的解决方案是(注意 \"$Line_1`n$Line_2\"
部分):
PowerShell -c Add-Type -AssemblyName System.Windows.Forms; ^
$Line_1 = 'Hello!'; ^
$Line_2 = 'How are you?'; ^
[System.Windows.Forms.MessageBox]::Show(\"$Line_1`n$Line_2\")
请注意,我明确添加了 -c
(-Command
) 参数名称以表示正在传递 PowerShell 命令字符串。虽然这在默认为 -Command
的 Windows PowerShell 中不是必需的,但在 PowerShell (Core) 7+[=247 中是必需的=],其中 -File
现在是默认值 - 请参阅 CLI 文档 了解 Windows PowerShell and PowerShell (Core) 7+。
也就是说,你必须在"..."
中使用$Line_1`n$Line_2
,一个expandable string,你必须\
-转义 "
个字符 以便 PowerShell 不会将它们作为命令行解析的一部分(在没有 overall double 的情况下-引用,"""
也有效)。
不幸的是,当使用 for /f
时,解析规则 发生变化 以便逐行处理 PowerShell 的输出 and/or 将其捕获到变量中:
注意:以下使用 [Console]::WriteLine()
来产生控制台输出,只是为了使用与 [System.Windows.Forms.MessageBox]::Show()
方法调用类似的语法,同时允许捕获某些内容通过 for /f
。在现实生活中,没有很好的理由调用[Console]::WriteLine()
。
for /f "delims=" %%l in ('
PowerShell -c Add-Type -AssemblyName System.Windows.Forms^; ^
$Line_1 ^= 'Hello!'^; ^
$Line_2 ^= 'How are you?'^; ^
[Console]::WriteLine^(\"$Line_1`n$Line_2\"^)
') do echo [%%l]
= , ; ( )
必须 另外 被转义(在 cmd.exe
视为 "..."
字符串的范围之外)。
如果您在 "..."
中另外包含一个 \"...\"
字符串以防止白色 space 规范化(请参阅下一节),您必须 ^
-escaped封闭(外部)"
;例如,
^"\"Marley & Me\"^"
续行(^
在命令内部行的末尾)实际上是 for /f
中的 可选 ,但它们包括在内以保持一致性。
引号转义要求总结:
你的带有续行的多行技术(^
在行尾)——语法上不能使用"..."
引用 entire 命令,因为 cmd.exe
不支持双引号多行 strings - 需要小心 ^
-转义应传递给 PowerShell 的所有 cmd.exe
元字符,特别是 & | < > ^
,此外, 如果从以下位置调用 PowerShell在 for /f
语句 、, ; = ( )
中 - 除非这些字符恰好是 cmd.exe
视为 double 的子字符串的一部分-引;例如,&
放在 \"...\"
字符串内 - 例如\"$Line_1`n & $Line_2\"
- 必须 而不是 被 ^
转义(但请参阅下面的重新白色 space 规范化)。
作为例外,元字符 %
必须始终转义为 %%
(仅适用于 批处理文件 ,不适用于命令提示符 - 请参阅 this answer).
此外,如果setlocal enabledelayedexpansion
生效(或者cmd.exe
以/V:ON
开始),!
也必须转义,但莫名其妙如下:
- As
^^!
(sic) 在 之外 cmd.exe
视为 "..."
字符串(其他元字符只需要 一个^
)
- 如
^!
里面这样的字符串(其他元字符需要无转义)
当通过 for /f
调用时,续行是 可选的 - 也就是说,您可以省略末尾的 ^
命令内部线。
每个语句必须以 ;
结尾(正如您所做的),因为行继续符 (^
) 导致 no 输入行之间的换行符,因此 PowerShell 将它们视为单行,多个语句必须 ;
分隔。
因为 cmd.exe
和 PowerShell 的 initial 命令行解析都不知道单引号字符串 ('...'
) 并且因为escaped \"...\"
字符串中的 "
字符在命令行解析时没有 syntactic 功能,这样的字符串被分解成 多个参数如果它们包含白色space:
实际上,多个相邻spaces在这些字符串中的运行被标准化为单个 space每个。例如,'How are you?'
和 \"How are you?\"
最终被 PowerShell 视为 'How are you?'
和 "How are you?"
。
为了避免这种情况,另外 将这些字符串包含在 "..."
中:
- 单-引号 PowerShell 字符串:
"'How are you?'"
- 注意:如果整个命令包含在
"..."
中,则不需要
- 双引号 PowerShell 字符串:
^"\"How are you?\"^"
。
^
-转义包含 "
个字符。确保 cmd.exe
仍然将内部 \"...\"
之间的内容视为双引号(因为它不将 \
识别为转义字符。),避免需要 ^
-在那里转义 cmd.exe
个元字符。
- 注意:如果整个命令包含在
"..."
中,则需要不同的方法:使用"... "^""How are you?"^"" ..."
和powershell.exe
(Windows PowerShell),以及 "... ""How are you?"" ..."
和 pwsh.exe
(PowerShell (Core) 7+)。
如果你想在 PowerShell 代码中包含 comments,你必须使用表单
^<# ... #^>
- 即(转义)内联 注释 - 不 支持正常的单行注释 (# ....
)(因为它需要 newline 来终止它们,但手头的调用方法中的语句之间没有换行符。
PowerShell 如何解析传递给其 CLI 的 -Command
/ -c
参数的参数:
PowerShell 让您可以选择传递命令字符串
或者:作为单个参数,包含在整体"..."
中;例如:
powershell -c "Get-Date -Format 'yyyy MMM'"
或:多个 个参数 - 可能单独 "..."
- 引用 - 然后 PowerShell 将其连接起来形成一个字符串;例如:
powershell -c Get-Date -Format "'yyyy MMM'"
在这两种情况下,未转义 "
个字符从参数中 剥离,因为它们被假定为为了 命令行 而不是生成的 PowerShell 命令,仅具有句法功能。
在剥离语法 "
字符并将结果参数与 spaces 连接后,如果适用,Powershell 将结果字符串 解释为 PowerShell 代码 。
注意:这与您使用 -File
CLI 参数调用 脚本文件 并将参数传递给它时解析参数的方式有根本不同 - 请参阅 更多。
我在CMD脚本文件中使用了以下代码
PowerShell Add-Type -AssemblyName System.Windows.Forms;^
$Line_1 = 'Hello!';^
$Line_2 = 'How are you?';^
[System.Windows.Forms.MessageBox]::Show($Line_1)
以上只会显示($Line_1)
如果使用 ($Line_1`n$Line_2)
,则不会显示任何内容。
如何让它同时显示 $Line_1
和 $Line_2
?
您可以查找多种连接字符串的方法,但最简单的方法可能是使用 + 符号。 `n 是一个换行符,它将第 2 行放在第 1 行下方。请注意,` 是一个反引号(通常与波浪号位于同一键上 ~)
[System.Windows.Forms.MessageBox]::Show($Line_1 + "`n" + $Line_2)
再次查看您的 post 后,我发现您与 ($Line_1`n$Line_2) 很接近。你只是缺少一些双引号
[System.Windows.Forms.MessageBox]::Show("$Line_1`n$Line_2")
Powershell 乐于用双引号内的值替换变量。您可以阅读有关可扩展字符串的更多信息
显然$Line_1 + "`n" + $Line_2
和"$Line_1`n$Line_2"
正常工作。将命令字符串从 cmd 发送到具有其遗留怪癖的 PowerShell 只是很棘手,因为:
- 在cmd中
()
是各个地方的特殊字符,表示一个块 - 令牌分隔符不仅是
<space>
和<tab>
,还有;
,
=
<0x0B>
<0x0C>
and<0xFF>
。这会更改 cmd 的标记化行为,但被调用的命令可能会再次使用其规则重新解析命令
根据文档,PowerShell 期望在最后一个参数 中使用 单个字符串 中的命令(这并不完全正确,因为文档未正确更新),因此您需要引用整个内容 或转义所有分隔符。最简单的解决方案是使用单行并像这样
"`n"
中的引号
PowerShell "Add-Type -AssemblyName System.Windows.Forms; $Line_1 = 'Hello!'; $Line_2 = 'How are you?'; [System.Windows.Forms.MessageBox]::Show($Line_1 + \"`n\" + $Line_2)"
如果你想将命令放在多行中,那么你不能引用字符串。现在要将整个内容作为一个参数,您需要转义所有空格(在这种情况下,您不需要以某种方式转义 ;
,可能是因为在将命令行传递给 PowerShell 后,它会调用 GetCommandLineW
并再次解析整个事情本身)
PowerShell Add-Type^ -AssemblyName^ System.Windows.Forms;^
$Line_1^ =^ 'Hello!';^
$Line_2^ =^ 'How^ are^ you?';^
[Windows.Forms.MessageBox]::Show($Line_1^ +^ \"`n\"^ +^ $Line_2)"
或者,您可以通过直接使用 [char]10
"`n"
字符串
PowerShell -Command Add-Type -AssemblyName System.Windows.Forms;^
$Line_1 = 'Hello!';^
$Line_2 = 'How are you?';^
[System.Windows.Forms.MessageBox]::Show($Line_1 + [char]10 + $Line_2)
最后一个无需任何转义即可工作的解决方案利用 PowerShell 的 EncodedCommand
选项接收 UTF-16 命令字符串的 base64 编码字符串。您可以通过 运行 在 PowerShell
$str = @'
>> Add-Type -AssemblyName System.Windows.Forms;
>> $Line_1 = 'Hello!';
>> $Line_2 = 'How are you?';
>> [System.Windows.Forms.MessageBox]::Show($Line_1 + "`n" + $Line_2)
>> '@
PS C:\Users\phucl> [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($str))
QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7AAoAIAA9ACAAJwBIAGUAbABsAG8AIQAnADsACgAgAD0AIAAnAEgAbwB3ACAAYQByAGUAIAB5AG8AdQA/ACcAOwAKAFsAUwB5AHMAdABlAG0ALgBXAGkAbgBkAG8AdwBzAC4ARgBvAHIAbQBzAC4ATQBlAHMAcwBhAGcAZQBCAG8AeABdADoAOgBTAGgAbwB3ACgAIAArACAAIgAKACIAIAArACAAKQA=
获得编码版本后,您可以从 cmd 调用它
PowerShell -EncodedCommand QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7AAoAJABMAGkAbgBlAF8AMQAgAD0AIAAnAEgAZQBsAGwAbwAhACcAOwAKACQATABpAG4AZQBfADIAIAA9ACAAJwBIAG8AdwAgAGEAcgBlACAAeQBvAHUAPwAnADsACgBbAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwAuAE0AZQBzAHMAYQBnAGUAQgBvAHgAXQA6ADoAUwBoAG8AdwAoACQATABpAG4AZQBfADEAIAArACAAIgBgAG4AIgAgACsAIAAkAEwAaQBuAGUAXwAyACkA
最简单的解决方案是(注意 \"$Line_1`n$Line_2\"
部分):
PowerShell -c Add-Type -AssemblyName System.Windows.Forms; ^
$Line_1 = 'Hello!'; ^
$Line_2 = 'How are you?'; ^
[System.Windows.Forms.MessageBox]::Show(\"$Line_1`n$Line_2\")
请注意,我明确添加了 -c
(-Command
) 参数名称以表示正在传递 PowerShell 命令字符串。虽然这在默认为 -Command
的 Windows PowerShell 中不是必需的,但在 PowerShell (Core) 7+[=247 中是必需的=],其中 -File
现在是默认值 - 请参阅 CLI 文档 了解 Windows PowerShell and PowerShell (Core) 7+。
也就是说,你必须在"..."
中使用$Line_1`n$Line_2
,一个expandable string,你必须\
-转义 "
个字符 以便 PowerShell 不会将它们作为命令行解析的一部分(在没有 overall double 的情况下-引用,"""
也有效)。
不幸的是,当使用 for /f
时,解析规则 发生变化 以便逐行处理 PowerShell 的输出 and/or 将其捕获到变量中:
注意:以下使用 [Console]::WriteLine()
来产生控制台输出,只是为了使用与 [System.Windows.Forms.MessageBox]::Show()
方法调用类似的语法,同时允许捕获某些内容通过 for /f
。在现实生活中,没有很好的理由调用[Console]::WriteLine()
。
for /f "delims=" %%l in ('
PowerShell -c Add-Type -AssemblyName System.Windows.Forms^; ^
$Line_1 ^= 'Hello!'^; ^
$Line_2 ^= 'How are you?'^; ^
[Console]::WriteLine^(\"$Line_1`n$Line_2\"^)
') do echo [%%l]
= , ; ( )
必须 另外 被转义(在cmd.exe
视为"..."
字符串的范围之外)。如果您在
"..."
中另外包含一个\"...\"
字符串以防止白色 space 规范化(请参阅下一节),您必须^
-escaped封闭(外部)"
;例如,
^"\"Marley & Me\"^"
续行(
^
在命令内部行的末尾)实际上是for /f
中的 可选 ,但它们包括在内以保持一致性。
引号转义要求总结:
你的带有续行的多行技术(
^
在行尾)——语法上不能使用"..."
引用 entire 命令,因为cmd.exe
不支持双引号多行 strings - 需要小心^
-转义应传递给 PowerShell 的所有cmd.exe
元字符,特别是& | < > ^
,此外, 如果从以下位置调用 PowerShell在for /f
语句 、, ; = ( )
中 - 除非这些字符恰好是cmd.exe
视为 double 的子字符串的一部分-引;例如,&
放在\"...\"
字符串内 - 例如\"$Line_1`n & $Line_2\"
- 必须 而不是 被^
转义(但请参阅下面的重新白色 space 规范化)。作为例外,元字符
%
必须始终转义为%%
(仅适用于 批处理文件 ,不适用于命令提示符 - 请参阅 this answer).此外,如果
setlocal enabledelayedexpansion
生效(或者cmd.exe
以/V:ON
开始),!
也必须转义,但莫名其妙如下:- As
^^!
(sic) 在 之外cmd.exe
视为"..."
字符串(其他元字符只需要 一个^
) - 如
^!
里面这样的字符串(其他元字符需要无转义)
- As
当通过
for /f
调用时,续行是 可选的 - 也就是说,您可以省略末尾的^
命令内部线。
每个语句必须以
;
结尾(正如您所做的),因为行继续符 (^
) 导致 no 输入行之间的换行符,因此 PowerShell 将它们视为单行,多个语句必须;
分隔。因为
cmd.exe
和 PowerShell 的 initial 命令行解析都不知道单引号字符串 ('...'
) 并且因为escaped\"...\"
字符串中的"
字符在命令行解析时没有 syntactic 功能,这样的字符串被分解成 多个参数如果它们包含白色space:实际上,多个相邻spaces在这些字符串中的运行被标准化为单个 space每个。例如,
'How are you?'
和\"How are you?\"
最终被 PowerShell 视为'How are you?'
和"How are you?"
。为了避免这种情况,另外 将这些字符串包含在
"..."
中:- 单-引号 PowerShell 字符串:
"'How are you?'"
- 注意:如果整个命令包含在
"..."
中,则不需要
- 注意:如果整个命令包含在
- 双引号 PowerShell 字符串:
^"\"How are you?\"^"
。^
-转义包含"
个字符。确保cmd.exe
仍然将内部\"...\"
之间的内容视为双引号(因为它不将\
识别为转义字符。),避免需要^
-在那里转义cmd.exe
个元字符。- 注意:如果整个命令包含在
"..."
中,则需要不同的方法:使用"... "^""How are you?"^"" ..."
和powershell.exe
(Windows PowerShell),以及"... ""How are you?"" ..."
和pwsh.exe
(PowerShell (Core) 7+)。
- 单-引号 PowerShell 字符串:
如果你想在 PowerShell 代码中包含 comments,你必须使用表单
^<# ... #^>
- 即(转义)内联 注释 - 不 支持正常的单行注释 (# ....
)(因为它需要 newline 来终止它们,但手头的调用方法中的语句之间没有换行符。
PowerShell 如何解析传递给其 CLI 的 -Command
/ -c
参数的参数:
PowerShell 让您可以选择传递命令字符串
或者:作为单个参数,包含在整体
"..."
中;例如:powershell -c "Get-Date -Format 'yyyy MMM'"
或:多个 个参数 - 可能单独
"..."
- 引用 - 然后 PowerShell 将其连接起来形成一个字符串;例如:powershell -c Get-Date -Format "'yyyy MMM'"
在这两种情况下,未转义 "
个字符从参数中 剥离,因为它们被假定为为了 命令行 而不是生成的 PowerShell 命令,仅具有句法功能。
在剥离语法 "
字符并将结果参数与 spaces 连接后,如果适用,Powershell 将结果字符串 解释为 PowerShell 代码 。
注意:这与您使用 -File
CLI 参数调用 脚本文件 并将参数传递给它时解析参数的方式有根本不同 - 请参阅