Suppress output from non-PowerShell commands? - powershell

I am running a command
hg st
and then checking it's $LASTEXITCODE to check for availability of mercurial in the current directory. I do not care about its output and do not want to show it to my users.
How do I suppress ALL output, success or error?
Since mercurial isn't a PowerShell commandlet hg st | Out-Null does not work.

Out-Null works just fine with non-PowerShell commands. However, it doesn't suppress output on STDERR, only on STDOUT. If you want to suppress output on STDERR as well you have to redirect that file descriptor to STDOUT before piping the output into Out-Null:
hg st 2>&1 | Out-Null
2> redirects all output from STDERR (file descriptor #2). &1 merges the redirected output with the output from STDOUT (file descriptor #1). The combined output is then printed to STDOUT from where the pipe can feed it into STDIN of the next command in the pipline (in this case Out-Null). See Get-Help about_Redirection for further information.

A fun thing you can do is to pipe the output to Write-Verbose, then you can still see it if you need it by running your script with the -Verbose switch.
ping -n 2 $APP 2>&1 | Write-Verbose

Can also do this
hg st *> $null
Powershell suppress console output

Related

Why does PowerShell interpret kind/kubectl STDOUT as STDERR and How to Prevent it?

We are moving our DevOps pipelines to a new cluster and while at it, we bumped into a weird behavior when calling kind with PowerShell. This applies to kubectl also.
The below should be taken only as a repro, not a real world application. In other words, I'm not looking to fix the below code but I am searching for an explanation why the error happens:
curl.exe -Lo kind-windows-amd64.exe https://kind.sigs.k8s.io/dl/v0.10.0/kind-windows-amd64
Move-Item .\kind-windows-amd64.exe c:\temp\kind.exe -Force
$job = Start-Job -ScriptBlock { iex "$args" } -ArgumentList c:\temp\kind.exe, get, clusters
$job | Receive-Job -Wait -AutoRemoveJob
Now, if I directly execute the c:\temp\kind.exe get clusters command in the PowerShell window, the error won't happen:
In other words, why does PowerShell (any version) consider the STDOUT of kind/kubectl as STDERR? And how can I prevent this from happening?
There must be an environmental factor to it as the same exact code runs fine in one system while on another it throws an error...
tl;dr
kind outputs its status messages to stderr, which in the context of PowerShell jobs surface via PowerShell's error output stream, which makes them print in red (and susceptible to $ErrorActionPreference = 'Stop' and -ErrorAction Stop).
Either:
Silence stderr: Use 2>$null as a general mechanism or, as David Kruk suggests, use a program-specific option to achieve the same effect, which in the case of kind is -q (--quiet)
Re-route stderr output through PowerShell's success output stream, merged with stdout output, using *>&1.
Caveat: The original output sequencing between stdout and stderr lines is not necessarily maintained on output.
Also, if you want to know whether the external program reported failure or success, you need to include the value of the automatic $LASTEXITCODE variable, which contains the most recently executed external program's process exit code, in the job's output (the exit code is the only reliably success/failure indicator - not the presence or absence of stderr output).
A simplified example with *>&1 (for Windows; on Unix-like platforms, replace cmd and /c with sh and -c):
$job = Start-Job -ScriptBlock {
param($exe)
& $exe $args *>&1
$LASTEXITCODE # Also output the process exit code.
} -ArgumentList cmd, /c, 'echo data1; echo status >&2; echo data2'
$job | Receive-Job -Wait -AutoRemoveJob
As many utilities do, kind apparently reports status messages via stderr.
Given that stdout is for data, it makes sense to use the only other available output stream, stderr, for anything that isn't data, so as to prevent pollution of the data output. The upshot is that stderr output doesn't necessarily indicate actual errors (success vs. failure should solely be inferred from an external program's process exit code).
PowerShell (for its own commands only) commendably has a more diverse system of output streams, documented in the conceptual about_Redirection help topic, allowing you to report status messages via Write-Verbose, for instance.
PowerShell maps an external program's output streams to its own streams as follows:
Stdout output:
Stdout output is mapped to PowerShell's success output stream (the stream with number 1, analogous to how stdout can be referred to in cmd.exe and POSIX-compatible shells), allowing it to be captured in a variable ($output = ...) or redirected to a file (> output.txt) or sent through the pipeline to another command.
Stderr output:
In local, foreground processing in a console (terminal), stderr is by default not mapped at all, and is passed through to the display (not colored in red) - unless a 2> redirection is used, which allows you to suppress stderr output (2>$null) or to send it to a file (2>errs.txt)
This is appropriate, because PowerShell cannot and should not assume that stderr output represents actual errors, whereas PowerShell's error stream is meant to be used for errors exclusively.
Unfortunately, as of PowerShell 7.2, in the context of PowerShell jobs (created with Start-Job or Start-ThreadJob) and remoting (e.g., in Invoke-Command -ComputerName ... calls), stderr output is mapped to PowerShell's error stream (the stream with number 2, analogous to how stdout can be referred to in cmd.exe and POSIX-compatible shells).
Caveat: This means that if $ErrorActionPreference = 'Stop' is in effect or -ErrorAction Stop is passed to Receive-Job or Invoke-Command, for instance, any stderr output from external programs will trigger a script-terminating error - even with stderr output comprising status messages only. Due to a bug in PowerShell 7.1 and below this can also happen in local, foreground invocation if a 2> redirection is used.
The upshot:
To silence stderr output, apply 2>$null - either at the source (inside the job or remote command), or on the receiving end.
To route stderr output (all streams) via the success output stream / stdout, i.e. to merge all streams, use *>&1
To prevent the stderr lines from printing in red (when originating from jobs or remote commands), apply this redirection at the source - which also guards against side effects from $ErrorActionPreference = 'Stop' / -ErrorAction Stop on the caller side.
Note: If you merge all streams with *>&1, the order in which stdout and stderr lines are output is not guaranteed to reflect the original output order, as of PowerShell 7.2.
If needed, PowerShell still allows you to later separate the output lines based on whether they originated from stdout or stderr - see this answer.

How to ignore specific error in PowerShell when executing a command?

I am aware ErrorAction argument, also $ErrorActionPreference,$Error and $.
Context
The issue I would like to solve is when running an external command (eg choco or git) it gives error which should be warning or not even warning, at least in my task context.
Because of their exit code PowerShell considers that result as error in any sense, for example writes out red to output, etc, which is not desirable in my task's context.
I can suppress those commands error output with -ErrorAction or 2> $null, but I have a bad feeling about completely vanishing any errors this way of the particular command.
I would like only ignore that known "not a problem in this context for me" type error.
Question
Is there any way to handle a command's error ignore some specific, but treat normally all other error conditions?
In a regular console window, in PowerShell v3 and above, stderr lines print just like stdout lines.
This is desirable, because stderr is used by many programs not just to report genuine errors, but anything that is not data, such as status messages.
Stderr lines (unless redirected - see below) print straight through the console (whereas stdout lines are sent to PowerShell's success output stream, where they can be collected in a variable, sent through the pipeline, or redirected with > / >>).
Regrettably, the PowerShell ISE, even in v5.1, does print stderr lines in red, in the same format as PowerShell errors.
Visual Studio Code with the PowerShell extension doesn't have this problem, and is worth migrating to in general, given that all future development effort will focus there, and given that it also works with the cross-platform PowerShell Core edition.
Well-behaved console applications solely use their exit code to signal success (exit code 0) vs. failure (any nonzero exit code). PowerShell saves the exit code of the most recently invoked console application in its automatic $LASTEXITCODE variable.
As an aside:
Common parameters -ErrorAction and -ErrorVariable cannot be used with external programs.
Similarly, preference variable $ErrorActionPreference has no effect (except accidentally, due to this bug, as of PowerShell v5.1 / PowerShell Core 6.2.0-preview.4).
try / catch cannot be used to detect and handle an external program's failure.
For a comprehensive overview of PowerShell's complex error handling rules, see this GitHub docs issue.
Note: I'm using the following external commands in the examples below, which produce 1 line of stdout and 1 line of stderr output (note that the redirection >&2 is handled by cmd, not PowerShell, because it is inside the '...'; it is used to produce stderr output):
cmd /c 'echo stdout & echo stderr >&2'
A variant that makes the command signal failure, via a nonzero exit code (exit 1):
cmd /c 'echo stdout & echo stderr >&2 & exit 1'
Therefore, normally the following should suffice:
$stdOut = cmd /c 'echo stdout & echo stderr >&2' # std*err* will print to console
if ($LASTEXITCODE -ne 0) { Throw "cmd failed." } # handle error
This captures stdout output in variable $stdout and passes stderr output through to the console.
If you want to collect stderr output for later and only show them in case of error:
$stdErr = #() # initialize array for collecting stderr lines.
# Capture stdout output while collecting stderr output in $stdErr.
$stdOut = cmd /c 'echo stdout & echo stderr >&2 & exit 1' 2>&1 | Where-Object {
$fromStdErr = $_ -is [System.Management.Automation.ErrorRecord]
if ($fromStdErr) { $stdErr += $_.ToString() }
-not $fromStdErr # only output line if it came from stdout
}
# Throw an error, with the collected stderr lines included.
if ($LASTEXITCODE -ne 0) {
Throw "cmd failed with the following message(s): $stdErr"
}
Note the 2>&1 redirection, which instructs PowerShell to send stderr lines (stream 2, the error stream) through the pipeline (stream 1, the success stream) as well.
In the Where-Object block, $_ -is [System.Management.Automation.ErrorRecord] is used to identify stderr lines, because PowerShell wraps such lines in instances of that type.
Alternatively, you could collect stderr output in a temporary file (2>/path/to/tmpfile), read its contents, and delete it.
This GitHub issue proposes introducing the option of collecting redirected stderr output in a variable, analogous to how you can ask cmdlets to collect errors in a variable via common parameter -ErrorVariable.
There are two caveats:
Inherently, by only printing stderr lines later, the specific context relative to the stdout output may be lost.
Due to a bug as of Windows PowerShell v5.1 / PowerShell Core 6.2.0, $ErrorActionPreference = Stop mustn't be in effect, because the 2>&1 redirection then triggers a script-terminating error as soon as the first stderr line is received.
If you want to selectively act on stderr lines as they're being received:
Note:
Inherently, as you're processing the lines, you won't yet know whether the program will report failure or success in the end.
The $ErrorActionPreference = 'Stop' caveat applies here too.
The following example filters out stderr line stderr1 and prints line stderr2 to the console in red (text only, not like a PowerShell error).
$stdOut = cmd /c 'echo stdout & echo stderr1 >&2 & echo stderr2 >&2' 2>&1 |
ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) { # stderr line
$stdErrLine = $_.ToString()
switch -regex ($stdErrLine) {
'stderr1' { break } # ignore, if line contains 'stderr1'
default { Write-Host -ForegroundColor Red $stdErrLine }
}
} else { # stdout line
$_ # pass through
}
}
# Handle error.
if ($LASTEXITCODE -ne 0) { Throw "cmd failed." }
I've come across this situation with git before, and worked around it by using $LASTEXITCODE.
Not sure if it's applicable to your situation, or to choco as I've not had issues with it.
# using git in powershell console sends everything to error stream
# this causes red text when it's not an error which is annoying
# 2>&1 redirects all to stdout, then use exit code to 'direct' output
$git_output = Invoke-Expression "& [git pull etc] 2>&1"
# handle output using exitcode
if ($LASTEXITCODE -ne 0) { throw $git_output }
else { Write-Output $git_output }

Is Out-Host buffering?

I ha a function, where I call an application with the & operator. The application produces several line command line output, downloads some files, and returns a string:
& "app.exe" | Out-Host
$var = ...
return $var
It seems, that on the console appears the output produced by app.exe only after app.exe terminates. The user does not have any real time information which file is downloading. Is there a way to continuously update the console when app.exe is running?
Many console applications buffer theirs output stream, if it known to be redirected. Actually, it is standard behavior of C library. So, buffering done by app.exe, because of redirection, but not by Out-Host.
Solution would be to not to redirect output of app.exe, even when outer command redirected. For than you should know exact condition, when PowerShell not redirect output stream of console application and link it directly to their own output stream, which would be console for interactive PowerShell.exe session. The conditions is:
Command is last item in pipeline.
Command is piped to Out-Default.
Solution would be wrap command into script block, and pipe that script block to Out-Default:
& { & "app.exe" } | Out-Default
The other solution would be to use Start-Process cmdlet with -NoNewWindow and -Wait parameters:
Start-Process "app.exe" -NoNewWindow -Wait

What does '>>' do in powershell?

When I run this command:
Get-Content generated\no_animate.css >> generated\all.css
It is copying the first files content into the second file. What is the official term for the >> symbol and where can I find more information about this and others?
Unsurprisingly searching for >> in Google is not terribly informative.
Thanks to EBGreen I found this information.
https://technet.microsoft.com/en-us/library/hh847746.aspx
TOPIC
about_Redirection
SHORT DESCRIPTION
Explains how to redirect output from Windows PowerShell to text files.
LONG DESCRIPTION
By default, Windows PowerShell sends its command output to the Windows
PowerShell console. However, you can direct the output to a text
file, and you can redirect error output to the regular output stream.
You can use the following methods to redirect output:
- Use the Out-File cmdlet, which sends command output to a text file.
Typically, you use the Out-File cmdlet when you need to use its
parameters, such as the Encoding, Force, Width, or NoClobber
parameters.
- Use the Tee-Object cmdlet, which sends command output to a text file
and then sends it to the pipeline.
- Use the Windows PowerShell redirection operators.
WINDOWS POWERSHELL REDIRECTION OPERATORS
The redirection operators enable you to send particular types of output
to files and to the success output stream.
The Windows PowerShell redirection operators use the following characters
to represent each output type:
* All output
1 Success output
2 Errors
3 Warning messages
4 Verbose output
5 Debug messages
NOTE: The All (*), Warning (3), Verbose (4) and Debug (5) redirection operators were introduced
in Windows PowerShell 3.0. They do not work in earlier versions of Windows PowerShell.
The Windows PowerShell redirection operators are as follows.
Operator Description Example
-------- ---------------------- ------------------------------
> Sends output to the Get-Process > Process.txt
specified file.
>> Appends the output to dir *.ps1 >> Scripts.txt
the contents of the
specified file.
2> Sends errors to the Get-Process none 2> Errors.txt
specified file.
2>> Appends errors to Get-Process none 2>> Save-Errors.txt
the contents of the
specified file.
2>&1 Sends errors (2) and Get-Process none, Powershell 2>&1
success output (1)
to the success
output stream.
3> Sends warnings to the Write-Warning "Test!" 3> Warnings.txt
specified file.
3>> Appends warnings to Write-Warning "Test!" 3>> Save-Warnings.txt
the contents of the
specified file.
3>&1 Sends warnings (3) and function Test-Warning
success output (1) { Get-Process PowerShell;
to the success Write-Warning "Test!" }
output stream. Test-Warning 3>&1
4> Sends verbose output to Import-Module * -Verbose 4> Verbose.txt
the specified file.
4>> Appends verbose output Import-Module * -Verbose 4>> Save-Verbose.txt
to the contents of the
specified file.
4>&1 Sends verbose output (4) Import-Module * -Verbose 4>&1
and success output (1)
to the success output
stream.
5> Sends debug messages to Write-Debug "Starting" 5> Debug.txt
the specified file.
5>> Appends debug messages Write-Debug "Saving" 5>> Save-Debug.txt
to the contents of the
specified file.
5>&1 Sends debug messages (5) function Test-Debug
and success output (1) { Get-Process PowerShell
to the success output Write-Debug "PS" }
stream. Test-Debug 5>&1
*> Sends all output types function Test-Output
to the specified file. { Get-Process PowerShell, none
Write-Warning "Test!"
*>> Appends all output types Write-Verbose "Test Verbose"
to the contents of the Write-Debug "Test Debug" }
specified file.
Test-Output *> Test-Output.txt
*>&1 Sends all output types Test-Output *>> Test-Output.txt
(*) to the success output Test-Output *>&1
stream.
The syntax of the redirection operators is as follows:
<input> <operator> [<path>\]<file>
If the specified file already exists, the redirection operators that do not
append data (> and n>) overwrite the current contents of the file without
warning. However, if the file is a read-only, hidden, or system file, the
redirection fails. The append redirection operators (>> and n>>) do not
write to a read-only file, but they append content to a system or hidden
file.
To force the redirection of content to a read-only, hidden, or system file,
use the Out-File cmdlet with its Force parameter. When you are writing to
files, the redirection operators use Unicode encoding. If the file has a
different encoding, the output might not be formatted correctly. To
redirect content to non-Unicode files, use the Out-File cmdlet with its
Encoding parameter.
SEE ALSO
Out-File
Tee-Object
about_Operators
about_Command_Syntax
about_Path_Syntax

PowerShell Tee-Object command with -Append

I run into an issue with the PowerShell 4.0 Tee-Object command (alias tee) and the command that I'm using is as follows:
powershell "cmd /c dir | Tee-Object -filepath C:\1.txt -Append"
When 1.txt is an empty file, it writes the output as you see on the screen (as-is). But if 1.txt has some existing content before you run the command, it appends the output with no new lines and the files looks completely messed up.
I'm surprised to see this behavior and any thoughts would help me to proceed further. Note that I have to use the Tee-Object command as I would like to see the output on the screen when a command is running and append it to the existing log file that has some contents.