通过Cmd将换行符传递给PowerShell

Passing newline character to PowerShell via Cmd

我正在尝试从Windows cmd.exe运行PowerShell脚本。 PowerShell脚本的输入是一个字符串,其中包含使用PowerShell反引号转义的换行符-即:

1
`r`n

出于演示目的,然后将输入字符串写入控制台,并转储到文件中。

我的问题是,使用语法从cmd.exe运行脚本时

1
powershell.exe script.ps1"TEST`r`nTEST"

字符串中的换行符不会被视为换行符,并且实际上包含在控制台输出和输出文本文件中。

1
TEST`r`nTEST

但是,如果我在PowerShell环境中运行此程序,则会得到预期的结果(即正确解析了换行符,并在适当的位置插入了换行符)。

1
2
TEST
TEST

同样,如果我通过Windows cmd.exe传递\
\
而不是转义的换行符,并在PowerShell脚本中执行.replace

1
2
3
$date = $data.replace("\
\
"
,"`r`n")

我得到了预期的输出:

1
2
TEST
TEST

有谁能够阐明为什么会发生这种情况?

测试脚本如下:

1
2
3
4
5
param([string]$data) # data to send

Write-Host $data
[IO.File]::WriteAllText('d:\\temp.txt', $data)
return 0

该文件从命令行调用为:

1
powershell.exe script.ps1"TEST`r`nTEST"

该脚本使用PowerShell v4.0在Windows Server 2012 R2上运行


tl; dr

使用-Command并将整个PowerShell命令作为单个字符串传递;例如。:

1
2
3
 C:\\> powershell -NoProfile -Command"script.ps1 \"TEST`r`nTEST\""
 TEST
 TEST

请注意,内部"实例是如何转义为\\"的,PowerShell在从外部调用时(或者,使用""")需要将其转义为\\"

在您的特定情况下,
powershell -NoProfile -Command script.ps1"TEST`r`nTEST"也可以,但是通常只有在字符串没有嵌入空格的情况下,它才能按预期工作。
鉴于-Command是PSv5.1的默认设置,您的命令-如当前发布的-应该保持原样。

从PowerShell v5.1开始,参数从外部传递给powershell.exe

  • 默认情况下以及使用-Command时,PowerShell会对其进行解释,包括字符串插值(即,当前不指定-File-Command都默认为-Command)。

    • 注意:默认行为将在v6中更改:-File将是默认行为,然后-在GitHub上查看相关更改。
  • 如果使用-File调用脚本,则不需要进行解释-(在cmd.exe可能解释之后)PowerShell将所有参数视为文字字符串。

    • 警告:目前正在针对v6讨论此行为,因为在至少一种情况下它存在明显的问题:尝试传递布尔值。

可选阅读:为什么在使用-Command时应将整个PowerShell命令作为单个参数传递:

当您将-Command与多个参数一起使用时,PowerShell本质上会将它们组装到幕后的单个命令行中,然后执行。

各个参数周围的所有"..."-引号都会在过程中丢失,这可能会导致意外的结果。例如。:

1
2
C:\\> powershell -NoProfile -Command"& { $args.count }""line 1`r`nline 2"
3   # !!"line 1`r`nline 2" was broken into 3 arguments

假定在解析命令行的过程中删除了外部"..."引用,那么PowerShell最终执行的实际命令行为:

1
2
 C:\\ PS> & { $args.Count } line 1`r`nline 2
 3

为了说明原因,让我们看一下使用显式引用的等效命令:

1
 C:\\ PS> & { $args.Count }"line"  "1`r`nline"  "2"

换句话说:除去封闭的"之后,像往常一样,将生成的令牌用空格分成多个参数。


回车符和换行符是具有值13和10的字节,您无法编写它们,也看不到它们。

为方便起见,在编写PowerShell代码时,该语言将允许您编写:

1
"`r`n"

用双引号引起来的字符串,并且在处理PowerShell源代码时(并且在其他时间),它将读取这些源代码并将其替换为字节值13和10。

正是PowerShell令牌生成器中的这一行代码可以实现。

cmd.exe解释器的backtick -n没什么特别的,把它放在字符串中也没什么特别的-您可以将它放在一个引号中

1
'`n'

或将其替换为字符串-除非必须注意替换发生的时间。例如在您的评论中:

For example, if you pass in 'r'n and then replace 'r'n with 'r'n, the 'r'n is still output literally

因为你的代码

1
-replace"`r`n"

变成

1
-replace"[char]13[char]10"

并且从外部传入的字符串包含

1
`r`n

他们不匹配。字符串中的反引号-n并不是魔术,PowerShell引擎不会将字符串全部解释为PowerShell代码,参数也不是。仅在这种情况下-编写-replace代码时,即发生实际换行符的交换。


该参数将需要重新解释为PowerShell字符串。这会帮助您前进吗?

您的-replace无效的原因是原始字符串实际上包含反引号。需要在搜索字符串中对其进行转义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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