Skip to content

Instantly share code, notes, and snippets.

@jhoneill
Created May 25, 2022 11:56
Show Gist options
  • Save jhoneill/b872f3e8a8433731a7373bdd99b00d72 to your computer and use it in GitHub Desktop.
Save jhoneill/b872f3e8a8433731a7373bdd99b00d72 to your computer and use it in GitHub Desktop.
function binaryout {
<#
.SYNOPSIS
Workaround for PowerShell processing the output of all external programs as strings
.DESCRIPTION
PowerShell treats any output from an external program as string which should be
split whenever it sees LF or CR LF. As a workround this calls the program with
Start-Process and redirects standard output to a file - on completion the file
is read and sent as a bytestream to the next process.
Optionally the file can be named and retained - like a tee operation, and when
this is done the output can be surpressed
.EXAMPLE
binaryout curl.exe "https://jhoneill.github.io/assets/james.jpg" | Set-Content -AsByteStream -Path .\james.jpg
Uses curl to get a jpg file and sends it to file - note that > is the equivalent of
Set-content without -AsByteSteam and will cause the bytes to be treated as strings
.EXAMPLE
binaryout curl.exe "https://jhoneill.github.io/assets/james.jpg" -teefile .\avatar.jpg -NoOutput
if the only requirement is to get a file then using the tee option with no output will do that.
#>
[cmdletbinding(PositionalBinding=$false,DefaultParameterSetName='MakeTmpFile')]
param (
#Filepath to pass to Start process (i.e. the command)
[Parameter(Position=0)]
[string]$FilePath,
#If specified keeps the temporary file effectively doing a TEE
[Parameter(ParameterSetName='UseTeeFile',Mandatory=$true)]
[String]$TeeFile,
#If specified with -TeeFile doesn't output the results
[Parameter(ParameterSetName='UseTeeFile')]
[switch]$NoOutput,
#Arguments to pass to Start-Process - collects the rest of the command line
[Parameter(ValueFromRemainingArguments)]
$Argumentlist
)
process {
if ($TeeFile) {$tempPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath( $TeeFile )}
else {$tempPath = [System.IO.Path]::GetTempFileName()}
Start-Process -NoNewWindow -FilePath $FilePath -ArgumentList $Argumentlist -RedirectStandardOutput $temppath
Write-Host ""
if (-not $NoOutput) {
$tries = 0
#The file can take a moment to close re-try at 1/10th sec intervals, give up after 10
while ($tries -lt 10) {
try { [System.IO.File]::ReadAllBytes($temppath)
break
}
catch {
$tries ++
Start-Sleep -Milliseconds 100
}
}
if (-not $TeeFile) {Remove-Item $tempPath -Force -ErrorAction SilentlyContinue}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment