问题
I have read here that when you run external commands in powershell, their output is always interpreted as a string or string array: https://stackoverflow.com/a/35980675/983442
I'm trying to process binary output from an external command, but it seems like PowerShell can only give me strings.
This leaves me wondering, what encoding is used to convert the binary data into strings? And also, how does it interpret newlines in order to divide the binary data into a string array? It seems to be splitting on the \n
character alone, but I'm sure it would also split on \r\n
.
Is there even a reliable way to take the strings powershell gives me and turn them back into a byte array?
For example, let's say I have a batch file with the following contents, call it thing.bat
:
@echo off
type image.jpg
I then run the following 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
How can I reliably recreate this image.jpg in PowerShell once I have the $x
variable?
回答1:
PowerShell assumes, that every external program called by you provides only string
s over it's output stream.
While this is not so far from reality, one might want to get the real bytes from an external program.
To achieve that, we will create a new process "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()
Which provides us a StreamReader for StandardOutput
and StandardError
, when the respective Redirect properties are set to $true
.
Now to get the stream's content we could easily use ReadToEnd() like $outContent = $proc.StandardOutput.ReadToEnd()
, but that would give us just a string again.
A StreamReader gives us the following methods (amongst others):
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()
Just create and pass a char[]
buffer to Read() and use it like you want:
$length = $proc.StandardOutput.Length
$s = New-Object 'char[]' $length
$proc.StandardOutput.Read($s, 0, $length - 1)
A second - easier but less flexible solution:
If you don't have a problem writing files to disk you could easily redirect the program's standard output to a file with -Encoding Oem
and read it in again with Get-Content
:
& .\thing.bat | Out-File -FilePath "C:/tmp/out.txt" -Encoding Oem
$rawContent = Get-Content -Path "C:/tmp/out.txt" -Encoding Oem
来源:https://stackoverflow.com/questions/46947168/how-to-get-original-binary-data-from-external-command-output-in-powershell