PowerShell Pipeline Output

I need to pass some data to stdin program.

  • The first 4 bytes are a 32-bit unsigned integer representing the length of the data. These 4 bytes are exactly the same as C will store the unsigned int in memory. I call it binary data.
  • The remaining bytes are 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 BinaryWriter (I think this code is right, I don't have C # lying right now ...)

 Bw.Write((int)Data.Length); Bw.Write(Data, 0, Data.Length); 

In PowerShell, I'm sure it is possible, but it is as close as possible. This obviously prints 4 size bytes in the form of 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 to be sent on the handset to look like this (\ xA is the escaped representation of the non-printable character, I don't want '\' in my output, I want BYTE '\ xA' to represent the output):

 \xA\x0\x0\0test data 

I do not know how to write a byte array from a pipeline in binary format. I also do not know how to get rid of the carriage return.

EDIT: I 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 creating a temporary file. There must be a better way!

EDIT: This solution above corrupts any character code> 127. There is no “binary” coding mode for the channel.

EDIT: I finally discovered a roundabout way to get a BinaryWriter connected to a stdin application. See my answer.

+7
binary powershell pipeline
source share
2 answers

Bill_Stewart is correct that you cannot transfer binary data. When do you use | operator, powershell uses the encoding dictated by $ OutputEncoding. I could not find an encoding that would not ruin the data.

I found something that works, BinaryWriter.

Here is my test code, starting with c: \ foo.exe, which simply displays the received data:

 #include <windows.h> #include <stdio.h> int main(int argc, char* argv[]) { HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE); BYTE aBuf[0x100]; int nRet; DWORD cbRead; if (!(nRet = ReadFile(hInput, aBuf, 256, &cbRead, NULL))) return printf("err: %u %d %d", cbRead, nRet, GetLastError()); for (int i=0 ; i<256 ; ++i) printf("%d ", aBuf[i]); return 0; } 

This ps script demonstrates "corruption":

 $file = "c:\foo.bin" Set-Content $file ([Byte[]](0..255)) -encoding byte $data = (Get-content $file -encoding byte) $prefix = ($data | foreach-object { $_ -as [char] }) -join "" "{0}" -f $prefix $OutputEncoding = [System.text.Encoding]::GetEncoding("us-ascii") $prefix | c:\foo.exe 

Here is the result. First you see that the $ prefix is ​​fully encoded. Secondly, you see that the data received in foo.exe has been converted.

  !"#$%&'()*+,-./0123456789:;<=> ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ [\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 5 0 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 9 7 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 

Using BinaryWriter works:

 $file = "c:\foo.bin" Set-Content $file ([Byte[]](0..255)) -encoding byte $data = (Get-content $file -encoding byte) $ProcessInfo = New-Object System.Diagnostics.ProcessStartInfo $ProcessInfo.FileName = "c:\foo.exe" $ProcessInfo.RedirectStandardInput = $true $ProcessInfo.RedirectStandardOutput = $true $ProcessInfo.UseShellExecute = $false $Proc = New-Object System.Diagnostics.Process $Proc.StartInfo = $ProcessInfo $Proc.Start() | Out-Null $Writer = new-object System.IO.BinaryWriter($proc.StandardInput.BaseStream); $Writer.Write($data, 0, $data.length) $Writer.flush() $writer.close() $Proc.WaitForExit() $Proc.StandardOutput.ReadToEnd() 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 5 0 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 9 7 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 1 33 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 16 8 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 

So my last script that writes the length in a binary before writing the data file would look something like this:

 $file = "c:\foo.bin" Set-Content $file ([Byte[]](0..255)) -encoding byte $data = (Get-content $file -encoding byte) $ProcessInfo = New-Object System.Diagnostics.ProcessStartInfo $ProcessInfo.FileName = "c:\foo.exe" $ProcessInfo.RedirectStandardInput = $true $ProcessInfo.RedirectStandardOutput = $true $ProcessInfo.UseShellExecute = $false $Proc = New-Object System.Diagnostics.Process $Proc.StartInfo = $ProcessInfo $Proc.Start() | Out-Null $Writer = new-object System.IO.BinaryWriter($proc.StandardInput.BaseStream); $Writer.Write([int]$data.length) $Writer.Write($data, 0, $data.length) $Writer.flush() $writer.close() $Proc.WaitForExit() $Proc.StandardOutput.ReadToEnd() 

you can see the first 4 bytes 0 1 0 0 - this is the original binary representation [int], equal to 256

 0 1 0 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 1 31 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 16 6 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 
+5
source share

Will this work for you?

 $fileName = "C:\test.txt" $data = [IO.File]::ReadAllText($fileName) $prefix = ([BitConverter]::GetBytes($data.Length) | foreach-object { "\x{0:X2}" -f $_ }) -join "" "{0}{1}" -f $prefix,$data 

You can replace "\x{0:X2}" -f $_ with $_ -as [Char] if you want $prefix contain raw representations of these bytes.

+1
source share

All Articles