tl;博士
:
从PowerShell 7.2开始,
如果你需要
原始字节处理
和/或需要防止PowerShell在特定情况下添加
尾随换行符
要删除文本数据,请避免使用
动力壳
整个管道。
对于原始字节处理,请使用
cmd
具有
/c
(在Windows上;在类Unix平台/类Unix Windows子系统上,使用
sh
或
bash
具有
-c
):
cmd /c 'type .\test.txt | .\Crypt.exe --encrypt | .\Crypt.exe --decrypt'
使用类似的技巧
将原始字节输出保存到
文件
-做
不
使用
动力壳
是的
>
接线员:
cmd /c 'someexe > file.bin'
注意
如果你想
捕获外部程序的
文本
输出
在PowerShell变量中
,你需要确保
[Console]::OutputEncoding
匹配程序的输出字符编码(通常是活动的OEM代码页),在本例中默认为true;详见下一节。
然而,一般来说,
字节
操纵
文本
最好避免使用数据。
有
二
单独的问题
,其中只有一个有简单的解决方案:
问题1
当前位置确实有一个角色
编码问题
,正如你所怀疑的:
无形中的PowerShell
在管道中插入自身作为中介,即使在向管道发送数据和从管道接收数据时也是如此
外部程序
:它
将数据从和转换为。网线
(
System.String
),它们是UTF-16代码单元的序列。
-
顺便提一下:即使只使用PowerShell本机命令,这也意味着从
文件夹
和
再次拯救他们
可能会导致不同的字符编码,因为在将(字符串)数据读入内存后,原始字符编码的信息不会被保留,而在保存数据时,cmdlet的
违约
使用的字符编码;而这种默认的编码方式始终是BOM较少的UTF-8
PowerShell(核心)6+
,它因中的cmdlet而异
Windows PowerShell
-看到了吗
this answer
.
为了发送和接收数据
外部程序
(例如
Crypt.exe
在你的情况下),你需要匹配
他们的
字符编码
; 在您的情况下,使用使用raw的Windows控制台应用程序
字节
处理时,隐含的编码是系统的活动OEM代码页。
-
在…上
发送
数据
,PowerShell使用
$OutputEncoding
偏好变量
编码
(通常被视为文本)数据,默认为ASCII(!)在Windows PowerShell中,以及在PowerShell(核心)中(无BOM)UTF-8。
-
这个
接收
终止
默认情况下包括:PowerShell使用
[控制台]::OutputEncoding
(这本身反映了
chcp
)对于解码接收到的数据,在Windows上,默认情况下,这反映了Windows PowerShell和PowerShell[Core]中的活动OEM代码页
[1]
.
因此,要解决主要问题,您需要
设置
$outpunten编码
至激活的OEM代码页
:
# Make sure that PowerShell uses the OEM code page when sending
# data to `.\Crypt.exe`
$OutputEncoding = [Console]::OutputEncoding
问题2
:
动力壳
总是附加一个尾随的换行符
将数据传输到外部程序时,要删除尚未包含的数据:
就是,
"foo" | .\Crypt.exe
不发送(消息)
$outpunten编码
-编码字节(表示)
"foo"
到
.\Crypt.exe
是stdin,它发送
"foo`r`n"
在窗户上;i、 例如,一个(适合平台的)换行符序列(Windows上的CRLF)会自动且不变地追加(除非字符串已经碰巧有一个尾随的换行符)。
这种有问题的行为将在中讨论
GitHub issue #5974
而且在
this answer
.
在您的特定情况下,隐式附加
"`r`n"
也受字节值移位的影响,这意味着
地窖。exe
把它转换成
-*
,导致
另一个
“`r`n”
当数据发送到第二个服务器时附加
地窖。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如何使用外部程序处理管道数据:
不像
命令
(或类似POSIX的外壳,例如
猛击
):
-
PowerShell不支持
原始字节数据
在管道中
.
[2]
-
当和
外部程序
它只知道
文本
(尽管它通过了.NET)
物体
当与PowerShell自己的命令交谈时(这是它的大部分功能的来源)。
具体来说,其工作原理如下:
-
当你
发送数据
到
外部程序
通过管道(至其标准数据流):
-
它是
皈依
文本
(字符串)使用
$outpunten编码
偏好变量
,默认为ASCII(!)在里面
Windows PowerShell
和(无BOM)UTF-8英寸
PowerShell(核心)
.
-
警告
:如果指定了编码
带BOM表
到
$outpunten编码
,PowerShell(从v7.0开始)将
发出BOM表
作为
第一
发送到外部程序的输出行;因此,例如,不要使用
[System.Text.Encoding]::Utf8
在Windows PowerShell中,并使用
[System.Text.Utf8Encoding]::new($false)
(事实并非如此)相反。
-
如果数据是
不
由PowerShell捕获或重定向,编码问题可能并不总是显而易见,也就是说,如果外部程序以使用
Windows Unicode控制台API
打印到显示屏上。
-
使用PowerShell的默认输出格式(与打印到控制台时看到的格式相同)将尚未为文本的内容(字符串)字符串化,并使用
重要警告
:
-
如果(最后一个)输入对象已存在
是
一个本身没有
尾随换行符
一个是
总是附加
(即使现有的尾随换行符也会被平台原生换行符替换,如果不同的话)。
-
当你
捕获/重定向数据
从…起
外部程序
(从它的标准流),它总是
解码为
一行行文字
(字符串),基于中指定的编码
[控制台]::OutputEncoding
,默认为Windows上的活动OEM代码页(令人惊讶的是
二者都
PowerShell版本,从v7开始。0-preview6
[1]
).
-
PowerShell内部文本使用表示。网
System.String
type
,它基于UTF-16代码单元(通常是松散的,但被错误地称为“Unicode”)
[3]
).
以上
也适用于
:
-
什么时候
管道数据
之间
外部程序
,
-
什么时候
数据是
重定向到文件
; 也就是说,无论数据来源及其原始字符编码如何,PowerShell都会使用
它的
向文件发送数据时的默认编码;在里面
Windows PowerShell
,
>
生成UTF-16LE编码的文件(带有BOM),而PowerShell(Core)明智地默认为无BOM的UTF-8(一致地,跨文件编写cmdlet)。
[1] 在PowerShell(核心)中,考虑到
$outpunten编码
值得称赞的是,它已经默认了UTF-8,因此有必要
[控制台]::OutputEncoding
保持相同-即,活动代码页有效
65001
在Windows上,如中所示
GitHub issue #7233
.
[2] 有来自
文件
,最接近原始字节处理的方法是将文件作为
网
System.Byte
大堆
具有
Get-Content -AsByteStream
(PowerShell(核心))/
Get-Content -Encoding Byte
(Windows PowerShell),但进一步处理(如阵列)的唯一方法是通过管道连接到
动力壳
用于处理字节数组或将其传递给。网络类型
方法
这需要一个字节数组。如果您试图将这样的数组发送到
外部程序
通过管道,
每个字节将作为其自身行上的十进制字符串表示形式发送
.
[3]
Unicode
摘要的名字是什么
标准
描述“全球字母表”。在具体使用中,它有各种标准
编码
UTF-8和UTF-16是应用最广泛的。