如何从 PowerShell 中的外部命令输出中获取原始二进制数据?

How to get original binary data from external command output in PowerShell?

我在这里读到,当您在 powershell 中 运行 外部命令时,它们的输出总是被解释为字符串或字符串数​​组:

我正在尝试处理来自外部命令的二进制输出,但 PowerShell 似乎只能提供字符串。

这让我想知道,使用什么编码将二进制数据转换为字符串?而且,它如何解释换行符以将二进制数据划分为字符串数组?它似乎单独在 \n 字符上拆分,但我敢肯定它也会在 \r\n.

上拆分

有没有一种可靠的方法可以将 powershell 提供的字符串转换回字节数组?

例如,假设我有一个包含以下内容的批处理文件,将其命名为 thing.bat:

@echo off
type image.jpg

然后我 运行 下面的 powershell:

PS> $x = & .\thing.bat
PS> $x.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array


PS> $x[0].gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object


PS> $x.count
36

一旦我有了 $x 变量,我如何才能在 PowerShell 中可靠地重新创建这个 image.jpg?

PowerShell 假定您调用的每个外部程序仅在其输出流上提供 strings。 虽然这与现实相去不远,但您可能希望从外部程序中获取真正的字节。 为此,我们将创建一个新流程 "from scratch"

$procInfo = New-Object System.Diagnostics.ProcessStartInfo -Property @{
    FileName = "cmd.exe"
    Arguments = "thing.bat"
    RedirectStandardError = $true
    RedirectStandardOutput = $true
    UseShellExecute = $false
}
$proc = New-Object System.Diagnostics.Process
$proc.StartInfo = $procInfo
$proc.Start() | Out-Null
$proc.WaitForExit()

当相应的重定向属性设置为 $true.

时,它为 StandardOutputStandardError 提供了 StreamReader

现在要获取流的内容,我们可以像 $outContent = $proc.StandardOutput.ReadToEnd() 一样轻松地使用 ReadToEnd(),但这只会再次给我们一个字符串。

A StreamReader 为我们提供了以下方法(以及其他方法):

Read             Method   int Read(), int Read(char[] buffer, int index, int count)
ReadAsync        Method   System.Threading.Tasks.Task[int] ReadAsync(char[] buffer, int index, int count)
ReadBlock        Method   int ReadBlock(char[] buffer, int index, int count)
ReadBlockAsync   Method   System.Threading.Tasks.Task[int] ReadBlockAsync(char[] buffer, int index, int count)
ReadLine         Method   string ReadLine()
ReadLineAsync    Method   System.Threading.Tasks.Task[string] ReadLineAsync()
ReadToEnd        Method   string ReadToEnd()
ReadToEndAsync   Method   System.Threading.Tasks.Task[string] ReadToEndAsync()

只需创建一个 char[] 缓冲区并将其传递给 Read() 并根据需要使用它:

$length = $proc.StandardOutput.Length
$s = New-Object 'char[]' $length
$proc.StandardOutput.Read($s, 0, $length - 1)

第二个 - 更简单但不太灵活的解决方案:

如果将文件写入磁盘没有问题,您可以使用 -Encoding Oem 轻松地将程序的标准输出重定向到一个文件,然后使用 Get-Content:[=30= 再次读取它]

& .\thing.bat | Out-File -FilePath "C:/tmp/out.txt" -Encoding Oem
$rawContent = Get-Content -Path "C:/tmp/out.txt" -Encoding Oem