Skip to content

Instantly share code, notes, and snippets.

@joshooaj
Forked from jhoneill/binaryout.ps1
Last active May 25, 2022 22:45
Show Gist options
  • Save joshooaj/94f44bfe029e16573b90c5962943ab40 to your computer and use it in GitHub Desktop.
Save joshooaj/94f44bfe029e16573b90c5962943ab40 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()
Write-Verbose "$($MyInvocation.MyCommand.Name) is writing to the temporary file $tempPath"
}
$startInfo = [System.Diagnostics.ProcessStartInfo]@{
FileName = $FilePath
Arguments = $Argumentlist
UseShellExecute = $false
CreateNoWindow = $true
RedirectStandardOutput = $true
RedirectStandardError = $ErrorActionPreference -eq 'Continue'
}
try {
$process = [System.Diagnostics.Process]::Start($startInfo)
$file = [io.file]::OpenWrite($tempPath)
$buffer = [byte[]]::new(4096)
while (0 -lt ($count = $process.StandardOutput.BaseStream.Read($buffer, 0, $buffer.Length))) {
$file.Write($buffer, 0, $count)
}
if ($ErrorActionPreference -eq 'Continue') {
$sb = [text.stringbuilder]::new()
while ($null -ne ($line = $process.StandardError.ReadLine())) {
$null = $sb.AppendLine($line)
}
if ($sb.Length -gt 0) {
Write-Error $sb.ToString()
}
}
$process.WaitForExit()
} finally {
if ($file) {
$file.Dispose()
}
if ($process) {
$process.Dispose()
}
}
if (-not $NoOutput) {
[io.file]::ReadAllBytes($tempPath)
if (-not $TeeFile) {
Write-Verbose "$($MyInvocation.MyCommand.Name) is deleting the temporary file $tempPath"
Remove-Item $tempPath -Force -ErrorAction SilentlyContinue
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment