I'm writing a script that will perform some git actions and I want to use Start-Transcript
to monitor it. The transcript is however missing most of the git
output.
I've tried to pipe the git
output to both Out-Host
and Out-Default
but neither work.
Here's my code...
$DateTime = Get-Date -Format "yyyyMMdd_HHmmss"
$TranscriptName = "PSTranscript_$DateTime"
Start-Transcript -Path ".\$TranscriptName.txt"
git --bare clone <GIT_REPO_URL> repo | Out-Default
Stop-Transcript
Here's my console output...
Transcript started, output file is .\PSTranscript_20191119_155424.txt
Cloning into 'repo'...
remote: Counting objects: 58975, done.
remote: Compressing objects: 100% (21457/21457), done.
remote: Total 58975 (delta 43348), reused 51727 (delta 37145)
Receiving objects: 100% (58975/58975), 70.46 MiB | 3.95 MiB/s, done.
Resolving deltas: 100% (43348/43348), done.
Checking out files: 100% (3878/3878), done.
Transcript stopped, output file is .\PSTranscript_20191119_155424.txt
And here's what is captured in the transcript, note that some of the git
output is missing
**********************
Windows PowerShell transcript start
Start time: 20191119155424
Username:
RunAs User:
Configuration Name:
Machine:
Host Application: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Process ID: 20644
PSVersion: 5.1.17134.858
PSEdition: Desktop
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.17134.858
BuildVersion: 10.0.17134.858
CLRVersion: 4.0.30319.42000
WSManStackVersion: 3.0
PSRemotingProtocolVersion: 2.3
SerializationVersion: 1.1.0.1
**********************
Transcript started, output file is .\PSTranscript_20191119_155424.txt
**********************
Windows PowerShell transcript end
End time: 20191119155457
**********************
There's no reason to call Out-Default
- just use your git
command as-is, and your problem will go away: both its stdout and stderr streams will be transcribed.
git
writes status information to stderr by default, and the - mistaken - use of Out-Default
affects stderr output specifically, as described in the bottom section.
The following example demonstrates that both stdout and stderr output from external programs are captured in the transcript:
$file = [IO.Path]::GetTempFileName()
"--- DIRECT OUTPUT ---`n"
Start-Transcript $file
# Execute a command that produces
# both stdout and stderr output.
# Do NOT use Out-Default.
if ($env:OS -eq 'Windows_NT') { # Windows
cmd /c 'echo hi & nosuch'
} else { # macOS, Linux
sh -c 'echo hi; nosuch'
}
Stop-Transcript
"`n--- CORE CONTENT OF TRANSCRIPT ---"
# Print the core of the transcript.
((Get-Content -Raw $file) -split '(?m)^\*+\s*$')[2]
Remove-Item $file
On Windows, you'll see something like the following, demonstrating that both streams were captured:
--- DIRECT OUTPUT ---
Transcript started, output file is C:\Users\jdoe\AppData\Local\Temp\tmp342C.tmp
hi
'nosuch' is not recognized as an internal or external command,
operable program or batch file.
Transcript stopped, output file is C:\Users\jdoe\AppData\Local\Temp\tmp342C.tmp
--- CORE CONTENT OF TRANSCRIPT ---
Transcript started, output file is C:\Users\jdoe\AppData\Local\Temp\tmp342C.tmp
hi
'nosuch' is not recognized as an internal or external command,
operable program or batch file.
Out-Default
cmdlet:Out-Default
is not meant to be called from user code.
Instead, it is meant to be used only by PowerShell host applications, including by PowerShell itself. It creates pretty-printed string representations of the output objects using PowerShell's output-formatting system and sends them to the host for display.
The reason that it is a public cmdlet is that you are allowed to override it in order to define a custom output formatter, although doing so is rare.
Overriding is as simple as defining an advanced Out-Default
function in your session, which must have the same parameter declarations and pipeline-binding behavior. You can scaffold such a function via a wrapper (proxy) function, as demonstrated in the bottom section of this answer). Note that in order to produce visible output from your function, you must send your custom-formatted strings either to the original Out-Default
or use the host APIs to print them (Write-Host
, in the simplest case).
Your use of Out-Default
- though ill-advised - should arguably still have worked.
While that is a moot point for Out-Default
, given that it shouldn't be called directly, Out-Host
exhibits the same problem (as of PowerShell Core 7.0.0-preview.6), and there are legitimate reasons to call Out-Host
, namely to explicitly print to the display only (and doing so should not affect transcription behavior).
Specifically, if you use Out-Host
explicitly, stderr output from external programs is unexpectedly not recorded in the transcript.
(By contrast, the problem doesn't surface with PowerShell commands.)
The problem has been reported in GitHub issue #11134.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With