gpt4 book ai didi

powershell - 通过CMD和PowerShell进行管道传输时,行为和输出不同

转载 作者:行者123 更新时间:2023-12-02 22:15:16 28 4
gpt4 key购买 nike

我正在尝试将文件的内容传递到我制作的简单ASCII对称加密程序中。这是一个简单的程序,可从STDIN读取输入,并对输入的每个字节加或减某个值(224)。
例如:如果第一个字节为4,而我们要加密,则它变为228。如果它超过255,则程序仅执行一些模运算。

这是我通过cmd获得的输出(test.txt包含“这是一个测试”):

    type .\test.txt | .\Crypt.exe --encrypt | .\Crypt.exe --decrypt
this is a test

它也以其他方式起作用,因此它是一种对称加密算法

    type .\test.txt | .\Crypt.exe --decrypt | .\Crypt.exe --encrypt
this is a test

但是,PowerShell上的行为有所不同。第一次加密时,我得到:

    type .\test.txt | .\Crypt.exe --encrypt | .\Crypt.exe --decrypt
this is a test_*

这就是我首先解密时得到的:

Screen Shot

也许是编码问题。提前致谢。

最佳答案

tl;博士:
如果需要原始字节处理和/或需要防止PowerShell偶尔在文本数据中添加尾随换行符,请完全避免使用PowerShell管道。
而是使用cmd封装为/c(在Windows上;在类似Unix的平台上,使用shbash-c):

cmd /c 'type .\test.txt | .\Crypt.exe --encrypt | .\Crypt.exe --decrypt'
请注意,如果要在PowerShell变量中捕获输出,则 必须确保[Console]::OutputEncoding.\Crypt.exe程序的(有效)输出编码( Activity 的OEM代码页面)匹配,这种情况下默认情况下应为true;有关详细信息,请参见下一部分。
但是,通常最好避免对文本数据进行字节操作。

有两个单独的问题,其中只有一个是简单的解决方案:

问题1 :您确实怀疑字符编码问题:
PowerShell会无形地将插入管道中,作为中介,即使在向外部程序发送数据或从外部程序接收数据时也是如此:它会将数据与.NET字符串(System.String)相互转换,该字符串是UTF-16代码单元的序列。
  • 顺便说一句:即使仅使用PowerShell本机命令,这也意味着从文件读取输入并再次保存它们会导致不同的字符编码,因为关于原始字符编码的信息不会保留一次(字符串)数据已被读取到内存中,并在保存时使用了cmdlet的默认字符编码;尽管此默认编码在PowerShell [Core] 6+中始终为无BOM的UTF-8,但Windows PowerShell中的cmdlet有所不同-请参见this answer

  • 为了发送和接收外部程序(例如Crypt.exe)的数据,您需要匹配它们的字符编码。在您的情况下,对于使用原始字节处理的Windows控制台应用程序,隐式编码是系统的 Activity OEM代码页。
  • 在发送数据时,PowerShell使用$OutputEncoding首选项变量的编码来编码(始终视为文本)数据,在Windows PowerShell中默认为ASCII(!),在PowerShell中默认为(无BOM)UTF-8 [Core ]。
  • 默认情况下覆盖接收端:PowerShell使用[Console]::OutputEncoding(它本身反射(reflect)chcp报告的代码页)来解码接收到的数据,并且在Windows上默认情况下反射(reflect) Activity 的OEM代码页,在Windows PowerShell和PowerShell中[Core] [1]。

  • 为了解决您的主要问题,因此,您需要$OutputEncoding设置为 Activity 的OEM代码页面:
    # Make sure that PowerShell uses the OEM code page when sending
    # data to `.\Crypt.exe`
    $OutputEncoding = [Console]::OutputEncoding

    问题2 :当将数据管道传输到外部程序时,PowerShell总是将尾随换行符附加到尚无新行的数据:
    也就是说,"foo" | .\Crypt.exe不会将$OutputEncoding(代表"foo"编码的字节)发送到.\Crypt.exe的stdin,而是在Windows上发送"foo`r`n"。也就是说,系统会自动且始终附加一个(适合平台的)换行符序列(在Windows上为CRLF)(除非该字符串已经碰巧有一个尾随的换行符)。
    this GitHub issuethis answer中讨论了这种有问题的行为。
    在您的特定情况下,隐式附加的"`r`n"也要进行字节值移位,这意味着第一个Crypt.exe调用将其转换为-*,从而在将数据发送到第二个"`r`n"调用时会附加另一个Crypt.exe
    最终结果是一个额外的换行符,该换行符是双向的(中间-*),加上一个加密的换行符,其结果为φΩ

    简而言之:如果输入数据中没有尾随换行符,则必须从结果中切除最后4个字符(代表往返和无意加密的换行符序列):
    # Ensure that .\Crypt.exe output is correctly decoded.
    $OutputEncoding = [Console]::OutputEncoding

    # Invoke the command and capture its output in variable $result.
    # Note the use of the `Get-Content` cmdlet; in PowerShell, `type`
    # is simply a built-in *alias* for it.
    $result = Get-Content .\test.txt | .\Crypt.exe --decrypt | .\Crypt.exe --encrypt

    # Remove the last 4 chars. and print the result.
    $result.Substring(0, $result.Length - 4)
    考虑到调用答案顶部所示的cmd /c也是可行的,这似乎不值得。

    PowerShell如何使用外部程序处理管道数据:
    cmd(或类似POSIX的 shell ,例如bash)不同:
  • PowerShell在管道中不支持原始字节数据。[2]
  • 在与外部程序对话时,它仅知道文本(而在与PowerShell自身的命令对话时,它会传递.NET对象,这正是它的强大功能来自何处)。

  • 具体来说,它的工作方式如下:
  • 当您通过管道(到其stdin流)将数据发送到外部程序时:
  • 使用 $OutputEncoding首选项变量中指定的字符编码将转换为文本(字符串),在Windows PowerShell中默认设置为ASCII(!),在PowerShell [Core]中默认为(BOM-less)UTF-8。
  • 注意::如果将带有BOM的编码分配给$OutputEncoding,PowerShell(从v7.0开始)将在发送到外部程序的第一行输出中发出BOM。因此,例如,不要在Windows PowerShell中使用[System.Text.Encoding]::Utf8(发出BOM),而应使用[System.Text.Utf8Encoding]::new($false)(不发出)。
  • 如果PowerShell不能捕获或重定向数据,则编码问题可能不会总是很明显,即,如果使用Windows Unicode控制台API来实现外部程序,则该问题可以通过打印到显示器来实现。

  • 使用PowerShell的默认输出格式(与打印到控制台时看到的格式相同)对尚未是文本(字符串)的内容进行字符串化处理,其中重要的注意事项是:
  • 如果(最后一个)输入对象已经是一个本身没有尾随换行符的字符串,则始终会附加(甚至将现有的尾随换行符替换为平台本机的换行符,如果不同)。
  • 此行为可能会导致问题,如this GitHub issuethis answer中所述。



  • 当您从外部程序(从其stdout流)捕获/重定向数据时,它总是根据 [Console]::OutputEncoding 中指定的编码,被解码为文本(字符串)行,默认编码为 Activity 的OEM Windows上的代码页(令人惊讶的是,两个PowerShell版本都从v7.0-preview6 [1]开始)。
  • PowerShell内部使用.NET System.String type表示文本,该文本基于UTF-16代码单元(通常较为宽松,但错误地称为“Unicode” [3])。

  • 上面的 也适用:
  • 当外部程序之间的管道数据时,
  • 数据重定向到文件时;也就是说,无论数据的来源和原始字符编码如何,PowerShell在将数据发送到文件时都使用其默认编码;在Windows PowerShell中,>生成UTF-16LE编码的文件(带有BOM),而PowerShell [Core]明智地默认为无BOM的UTF-8(在整个文件编写cmdlet中始终如此)。

  • this GitHub issue的主题是增加对在外部程序和文件重定向之间传递的原始数据的支持。

    [1]在PowerShell [Core]中,考虑到$OutputEncoding缺省已经默认为UTF-8,应该使[Console]::OutputEncoding相同是有意义的-即,如this GitHub issue中所述, Activity 代码页在Windows上有效地为65001
    [2]通过文件输入,最接近原始字节处理的是使用System.Byte(PowerShell [Core])/ Get-Content -AsByteStream(Windows PowerShell)将文件读取为.NET Get-Content -Encoding Byte数组,但唯一的方法是诸如数组之类的进一步过程是通过管道传递到旨在处理字节数组的PowerShell命令,或者通过将其传递给期望字节数组的.NET类型的方法。如果您试图通过管道将这样的数组发送到外部程序,则每个字节将以其十进制字符串表示形式在其自己的行上发送。
    [3] Unicode是描述“全局字母”的抽象标准的名称。在具体使用中,它具有各种标准编码,其中UTF-8和UTF-16是使用最广泛的。

    关于powershell - 通过CMD和PowerShell进行管道传输时,行为和输出不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59110563/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com