【问题标题】:Piping Text To An External Program Appends A Trailing Newline将文本传送到外部程序会附加尾随换行符
【发布时间】:2018-06-30 11:39:48
【问题描述】:

我一直在比较多个系统之间的哈希值,惊讶地发现 PowerShell 的哈希值与其他终端不同。

Linux 终端(CygWin、Bash for Windows 等)和 Windows 命令提示符都显示相同的哈希值,而 PowerShell 显示不同的哈希值。

这是使用 SHA256 进行测试的,但在使用其他算法(如 md5)时发现了同样的问题。

编码更新:

尝试更改 PShell 编码,但对返回的哈希值没有任何影响。

[Console]::OutputEncoding.BodyName 
iso-8859-1
[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8
utf-8

GitHub PowerShell 问题

https://github.com/PowerShell/PowerShell/issues/5974

【问题讨论】:

标签: linux powershell hash pipe newline


【解决方案1】:

tl;博士:

当 PowerShell 将字符串传送到外部程序时:

因此,关键是避免PowerShell的管道转而使用本机shell,从而防止隐式添加尾随换行符

  • 如果您在类似 Unix 的平台上运行命令(使用 PowerShell Core):
sh -c "printf %s 'string' | openssl dgst -sha256 -hmac authcode"

printf %secho -n便携替代品。如果字符串包含 ' 字符。加倍它们或使用 `"...`" 引用代替。

  • 如果您需要通过 cmd.exeWindows 上执行此操作,事情会变得更加棘手,因为 cmd.exe 不直接支持没有尾随换行符的回显:
cmd /c "<NUL set /p =`"string`"| openssl dgst -sha256 -hmac authcode"

请注意,| 之前必须有 没有空格 才能正常工作。有关此解决方案的说明和限制,请参阅this answer

仅当字符串包含非 ASCII 字符您在 Windows PowerShell 中运行时,才会出现编码问题;在这种情况下,首先将 $OutputEncoding 设置为目标实用程序期望的编码,通常是 UTF-8:$OutputEncoding = [Text.Utf8Encoding]::new()


  • PowerShell,从 Windows PowerShell v5.1 / PowerShell (Core) v7.2 开始,总是在您发送一个不带换行符的字符串时附加一个尾随换行符通过管道到 external 实用程序,这是您观察到差异的原因(尾随换行符仅在 Unix 平台上是 LF,在 Windows 上是 CRLF 序列) .

  • 此外,当涉及到将数据传送到外部程序时,PowerShell 的管道总是基于文本;内部基于 UTF-16LE 的 PowerShell (.NET) 字符串根据存储在自动 $OutputEncoding 变量中的编码进行转码,该变量在 Windows PowerShell 中默认为仅 ASCII 编码,并且为 UTF- PowerShell Core 中的 8 编码(在 Windows 和类 Unix 平台上)。

  • PowerShell 中的echo -n 确实不会 生成没有尾随换行符的字符串,因此偶然 是您的问题;为了完整起见,这里有一个解释:

    • echo 是 PowerShell 的 Write-Output cmdlet 的别名,它 - 在管道到 external 程序的上下文中 - 将 text 写入程序的标准输入em>next 管道段(类似于 Bash / cmd.exe 的echo)。
    • -n 被解释为Write-Output-NoEnumerate 开关的(明确)缩写。
    • -NoEnumerate 只适用于写多个对象的时候,所以这里没有作用。
    • 因此,简而言之:在 PowerShell 中,echo -n "string"Write-Output -NoEnumerate "string" 相同,因为仅输出单个字符串,因此与 Write-Output "string" 相同,而 Write-Output "string" 又与刚才相同使用 "string",依赖 PowerShell 的 implicit 输出行为。
    • Write-Output 没有抑制尾随换行符的选项,即使这样做了,使用管道将其通过管道连接到 外部 程序也会将其重新添加。

【讨论】:

    【解决方案2】:

    Linux 终端和 PowerShell 使用不同的编码。所以echo -n "string" 产生的真实字节是不同的。我在我的 Linux Mint 终端和 Windows 10 PowerShell 上进行了尝试。这是我得到的:

    Linux Mint:

    73 74 72 69 6E 67
    

    Windows 10:

    FF FE 73 00 74 00 72 00 69 00 6E 00 67 00 0D 00 0A 00
    

    似乎 Linux 终端使用 UTF-8,而 Windows PowerShell 使用带有 BOM 的 UTF-16。同样在 PowerShell 中,您不能将“-n”参数用于回显。所以 echo 将换行符 \r\n (0D 00 0A 00) 放在“字符串”的末尾。

    编辑:正如下面mklement0 所说,Windows PowerShell 在管道时默认使用 ASCII。

    【讨论】:

    • 我不是 PowerShell 人,但我所看到的关于它的一切对于一个希望它像 shell 脚本一样具有 Unix/Linux 背景的人来说都是可怕的。一旦我看到我必须为简单的grepsed 做些什么,我就放弃了 PowerShell。
    • PowerShell 在管道 到外部程序时确实 使用UTF-16LE,至少在默认情况下是这样。自动$OutputEncoding 变量确定管道到外部程序时使用的编码,例如openssl,在 Windows PowerShell 中默认为 ASCII,在 PowerShell Core 中默认为 UTF-8我>。确实是总是添加的尾随换行符导致了这里的问题;您可以在 PowerShell 中使用 -necho,但其语义不同(echo -n 等效于 Write-Output -NoEnumerate)。
    • @jwww:从历史上讲,PowerShell 习惯于成为自己的世界;随着它向跨平台的过渡,在与外部实用程序交互时,它仍然需要学习一些东西,这些在 Unix 世界中比在 Windows 世界中重要得多。我鼓励您通过github.com/PowerShell/PowerShell/issues 帮助该过程
    • @Pie:谢谢;回顾一下:真正的问题是 PowerShell 管道 本身 当管道传输到外部程序时 会附加一个尾随换行符;这不是 encoding 问题(可能在 Windows PowerShell 中使用包含 non-ASCII 字符的字符串)。因为这个答案侧重于编码(并且歪曲了转码如何与 外部 程序一起工作),所以它是我的 -1。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-20
    • 1970-01-01
    • 2013-08-19
    相关资源
    最近更新 更多