I need to pipe some data to a program's stdin.
- First 4 bytes are a 32 bit unsigned integer representing the length of the data. These 4 bytes are exactly the same as C would store an unsigned int in memory. I refer to this as binary data.
- Remaining bytes are the data.
In c, this is trivial:
WriteFile(h, &cb, 4); // cb is a 4 byte integer
WriteFile(h, pData, cb);
or
fwrite(&cb, sizeof(cb), 1, pFile);
fwrite(pData, cb, 1, pFile);
or c# you would use a BinaryWriter (I think this code is right, i don't have c# lying around right now...)
Bw.Write((int)Data.Length);
Bw.Write(Data, 0, Data.Length);
In PowerShell I'm sure it's possible, but this is as close as I could get. This is obviously printing out the 4 bytes of the size as 4 human readable numbers:
$file = "c:\test.txt"
Set-content $file "test data" -encoding ascii
[int]$size = (Get-ChildItem $file).Length
$bytes = [System.BitConverter]::GetBytes($size)
$data = Get-content $file
$bytes
$data
11
0
0
0
test data
I need the binary data sent out on the pipe to look like this (\xA is the escaped representation of a non printable character, I don't want '\' in my output, I want the BYTE that '\xA' represents in the output) :
\xA\x0\x0\0test data
I don't know how to write a byte array out the pipeline in binary format. I also don't know how to get rid of the carriage returns.
EDIT: I have found that I can do this:
$file = "c:\test.txt"
Set-content $file "test data" -encoding ascii
"File: ""{0}""" -f (Get-content $file)
[int]$size = (Get-ChildItem $file).Length
"Size: " + $size
$bytes = [System.BitConverter]::GetBytes($size)
"Bytes: " + $bytes
$data = Get-content $file
$file1 = "c:\test1.txt"
Set-content $file1 $bytes -encoding byte
Add-Content $file1 $data -encoding ASCII
"File: ""{0}""" -f (Get-content $file1)
"Size: " + (Get-ChildItem $file1).Length
File: "test data"
Size: 11
Bytes: 11 0 0 0
File: " test data"
Size: 15
But this requires me to build a temporary file. There must be a better way!
EDIT: That solution above, corrupts any character code > 127. There is no "binary" encoding mode for the pipe.
EDIT: I finally discovered a roundabout way to get a BinaryWriter wired up to an app's stdin. See my answer.
Bill_Stewart is correct that you can't pipe binary data. When you use the | operator, powershell uses the encoding dictated by $OutputEncoding. I could not find an encoding that would not corrupt data.
I found something that does work though, BinaryWriter.
Here is my test code, starting with c:\foo.exe that simply outputs the data it receives:
This ps script demonstrates the "corruption":
Here is the output. First you see that $prefix does have the complete charset. Second, you see the data that got to foo.exe has been converted.
Using BinaryWriter works:
So, my final script which writes the length in binary before writing the data file, would look something like this:
you can see the first 4 bytes 0 1 0 0 are the raw binary representation of an [int] that is equal to 256
Would this work for you?
You can replace
"\x{0:X2}" -f $_
with$_ -as [Char]
if you want$prefix
to contain the raw data representations of the bytes.