通过 Cmd 将换行符传递给 PowerShell
Passing newline character to PowerShell via Cmd
我正在尝试 运行 来自 Windows cmd.exe 的 PowerShell 脚本。 PowerShell 脚本的输入是一个字符串,其中包含使用 PowerShell 反引号转义的换行符 - 即:
`r`n
出于演示目的,输入字符串随后被写入控制台,同时也被转储到文件中。
我遇到的问题是,当脚本是 cmd.exe 中的 运行 使用语法
powershell.exe script.ps1 "TEST`r`nTEST"
字符串中的换行符不被视为换行符,而是按字面意思包含在控制台输出和输出文本文件中。
TEST`r`nTEST
但是,如果我 运行 从 PowerShell 环境中执行此操作,我会得到预期的结果(即正确解析换行符,并在适当的位置插入换行符)。
TEST
TEST
同样,如果我通过 Windows cmd.exe 传入 \r\n
而不是转义的换行符,并在 PowerShell 脚本
中执行 .replace
]
$date = $data.replace("\r\n","`r`n")
我得到了预期的输出:
TEST
TEST
有人能解释一下为什么会这样吗?
测试脚本如下:
param([string]$data) # data to send
Write-Host $data
[IO.File]::WriteAllText('d:\temp.txt', $data)
return 0
并且从命令行调用该文件为:
powershell.exe script.ps1 "TEST`r`nTEST"
脚本 运行ning 在 Windows Server 2012 R2 上,使用 PowerShell v4.0
参数将需要重新解释为 PowerShell 字符串。这会让你走上正轨吗?
您的 -replace
不起作用的原因是原始字符串实际上包含反引号。需要在搜索字符串中进行转义。
C:\src\t>type p1.ps1
Param([string]$s)
Write-Host $s
$p = Invoke-Expression `"$s`"
Write-Host $p
$p2 = $s -replace "``r``n","`r`n"
Write-Host $p2
C:\src\t>powershell -noprofile -file .\p1.ps1 "TEST`r`nTEST"
TEST`r`nTEST
TEST
TEST
TEST
TEST
Carriagereturn和Linefeed是字节,值为13和10,不能写,看不到。
为方便起见,在编写 PowerShell 代码时,该语言会让您这样写:
"`r`n"
在双引号字符串中,在处理 PowerShell 源代码时(而不是在其他时间),它会读取这些代码并将它们替换为字节值 13 和 10。
在 PowerShell 分词器中 this line of code 执行此操作。
对于 cmd.exe 解释器来说,backtick-n 没有什么特别之处,将它放在字符串中也没有什么特别之处 - 您可以将它放在单引号字符串中
'`n'
或在字符串中替换它 - 除了您必须注意替换发生的时间。例如在您的评论中:
For example, if you pass in 'r'n and then replace 'r'n with 'r'n, the 'r'n is still output literally
因为你的代码
-replace "`r`n"
变成
-replace "[char]13[char]10"
而你从外部传入的字符串包含
`r`n
而且它们不匹配。字符串中的 Backtick-n 并不神奇,PowerShell 引擎不会将所有字符串都解释为 PowerShell 代码,也不是参数,或任何其他内容。并且只有在这种情况下 - 当您编写 -replace 代码时,即发生 实际换行符 的交换。
tl;dr
使用-Command
并将整个 PowerShell命令作为单个字符串传递;例如:
C:\> powershell -NoProfile -Command "script.ps1 \"TEST`r`nTEST\""
TEST
TEST
注意内部 "
实例如何转义为 \"
,当从 外部 调用时,PowerShell 需要这样做(或者,为了完全稳健,使用"^""
(sic) 在 Windows PowerShell 和 ""
在 PowerShell (Core) v6+).
在您的具体案例中,
powershell -NoProfile -Command script.ps1 "TEST`r`nTEST"
也可以工作,但通常只有在字符串没有嵌入 spaces.
时才会按预期工作
鉴于 -Command
是 PSv5.1 的 默认 ,您的命令 - 如当前发布的那样 - 应该按原样。
从 PowerShell v5.1 开始,从外部传递给 powershell.exe
的参数:
由 PowerShell 进行解释,包括字符串插值,默认 以及当您使用 -Command
(即既不指定 -File
也不指定 -Command
当前默认为 -Command
)。
- 警告:默认行为将在 v6 中更改:
-File
将成为默认行为 - 请参阅 relevant change on GitHub.
不受解释 如果您使用 -File
调用脚本 -(在cmd.exe
的潜在解释)PowerShell 将所有参数视为 文字字符串 .
- 警告:此行为是 currently being discussed 相对于 v6,因为它至少在一个方面存在明显问题案例:尝试传递 布尔值 值。
可选阅读:为什么在使用 -Command
时应将 entire PowerShell 命令作为 single 参数传递:
当您将 -Command
与 多个 参数一起使用时,PowerShell 本质上是在执行之前将它们组合到幕后的单个命令行中。
任何 "..."
围绕单个参数的引用都会在这个过程中 丢失,这可能会产生意想不到的结果;例如:
C:\> powershell -NoProfile -Command "& { $args.count }" "line 1`r`nline 2"
3 # !! "line 1`r`nline 2" was broken into 3 arguments
鉴于在解析命令行的过程中删除了外部 "..."
引号,PowerShell 最终执行的实际命令行是:
C:\ PS> & { $args.Count } line 1`r`nline 2
3
为了说明原因,让我们看一个使用显式引号的等效命令:
C:\ PS> & { $args.Count } "line" "1`r`nline" "2"
换句话说:在删除封闭的 "
之后,生成的标记被 空格 分成 多个 个参数, 和往常一样。
我正在尝试 运行 来自 Windows cmd.exe 的 PowerShell 脚本。 PowerShell 脚本的输入是一个字符串,其中包含使用 PowerShell 反引号转义的换行符 - 即:
`r`n
出于演示目的,输入字符串随后被写入控制台,同时也被转储到文件中。
我遇到的问题是,当脚本是 cmd.exe 中的 运行 使用语法
powershell.exe script.ps1 "TEST`r`nTEST"
字符串中的换行符不被视为换行符,而是按字面意思包含在控制台输出和输出文本文件中。
TEST`r`nTEST
但是,如果我 运行 从 PowerShell 环境中执行此操作,我会得到预期的结果(即正确解析换行符,并在适当的位置插入换行符)。
TEST
TEST
同样,如果我通过 Windows cmd.exe 传入 \r\n
而不是转义的换行符,并在 PowerShell 脚本
.replace
]
$date = $data.replace("\r\n","`r`n")
我得到了预期的输出:
TEST
TEST
有人能解释一下为什么会这样吗?
测试脚本如下:
param([string]$data) # data to send
Write-Host $data
[IO.File]::WriteAllText('d:\temp.txt', $data)
return 0
并且从命令行调用该文件为:
powershell.exe script.ps1 "TEST`r`nTEST"
脚本 运行ning 在 Windows Server 2012 R2 上,使用 PowerShell v4.0
参数将需要重新解释为 PowerShell 字符串。这会让你走上正轨吗?
您的 -replace
不起作用的原因是原始字符串实际上包含反引号。需要在搜索字符串中进行转义。
C:\src\t>type p1.ps1
Param([string]$s)
Write-Host $s
$p = Invoke-Expression `"$s`"
Write-Host $p
$p2 = $s -replace "``r``n","`r`n"
Write-Host $p2
C:\src\t>powershell -noprofile -file .\p1.ps1 "TEST`r`nTEST"
TEST`r`nTEST
TEST
TEST
TEST
TEST
Carriagereturn和Linefeed是字节,值为13和10,不能写,看不到。
为方便起见,在编写 PowerShell 代码时,该语言会让您这样写:
"`r`n"
在双引号字符串中,在处理 PowerShell 源代码时(而不是在其他时间),它会读取这些代码并将它们替换为字节值 13 和 10。
在 PowerShell 分词器中 this line of code 执行此操作。
对于 cmd.exe 解释器来说,backtick-n 没有什么特别之处,将它放在字符串中也没有什么特别之处 - 您可以将它放在单引号字符串中
'`n'
或在字符串中替换它 - 除了您必须注意替换发生的时间。例如在您的评论中:
For example, if you pass in 'r'n and then replace 'r'n with 'r'n, the 'r'n is still output literally
因为你的代码
-replace "`r`n"
变成
-replace "[char]13[char]10"
而你从外部传入的字符串包含
`r`n
而且它们不匹配。字符串中的 Backtick-n 并不神奇,PowerShell 引擎不会将所有字符串都解释为 PowerShell 代码,也不是参数,或任何其他内容。并且只有在这种情况下 - 当您编写 -replace 代码时,即发生 实际换行符 的交换。
tl;dr
使用-Command
并将整个 PowerShell命令作为单个字符串传递;例如:
C:\> powershell -NoProfile -Command "script.ps1 \"TEST`r`nTEST\""
TEST
TEST
注意内部 "
实例如何转义为 \"
,当从 外部 调用时,PowerShell 需要这样做(或者,为了完全稳健,使用"^""
(sic) 在 Windows PowerShell 和 ""
在 PowerShell (Core) v6+).
在您的具体案例中,
powershell -NoProfile -Command script.ps1 "TEST`r`nTEST"
也可以工作,但通常只有在字符串没有嵌入 spaces.
时才会按预期工作
鉴于 -Command
是 PSv5.1 的 默认 ,您的命令 - 如当前发布的那样 - 应该按原样。
从 PowerShell v5.1 开始,从外部传递给 powershell.exe
的参数:
由 PowerShell 进行解释,包括字符串插值,默认 以及当您使用
-Command
(即既不指定-File
也不指定-Command
当前默认为-Command
)。- 警告:默认行为将在 v6 中更改:
-File
将成为默认行为 - 请参阅 relevant change on GitHub.
- 警告:默认行为将在 v6 中更改:
不受解释 如果您使用
-File
调用脚本 -(在cmd.exe
的潜在解释)PowerShell 将所有参数视为 文字字符串 .- 警告:此行为是 currently being discussed 相对于 v6,因为它至少在一个方面存在明显问题案例:尝试传递 布尔值 值。
可选阅读:为什么在使用 -Command
时应将 entire PowerShell 命令作为 single 参数传递:
当您将 -Command
与 多个 参数一起使用时,PowerShell 本质上是在执行之前将它们组合到幕后的单个命令行中。
任何 "..."
围绕单个参数的引用都会在这个过程中 丢失,这可能会产生意想不到的结果;例如:
C:\> powershell -NoProfile -Command "& { $args.count }" "line 1`r`nline 2"
3 # !! "line 1`r`nline 2" was broken into 3 arguments
鉴于在解析命令行的过程中删除了外部 "..."
引号,PowerShell 最终执行的实际命令行是:
C:\ PS> & { $args.Count } line 1`r`nline 2
3
为了说明原因,让我们看一个使用显式引号的等效命令:
C:\ PS> & { $args.Count } "line" "1`r`nline" "2"
换句话说:在删除封闭的 "
之后,生成的标记被 空格 分成 多个 个参数, 和往常一样。