tl;dr:
Use 2>$null
to suppress the stderr output from a call to an external program (such as schtasksk.exe
)
- To work around a bug present up to at least PowerShell [Core] 7.0 (see below), make sure that
$ErrorActionPreferece
is not set to 'Stop'
.
# Execute with stderr silenced.
# Rely on the presence of stdout output in the success case only
# to make the conditional true.
if (schtasks /query /TN 'SOMETASK' 2>$null) { # success, task exists
"Processing removal of scheduled task`n"
# ...
}
For background information and more general use cases, read on.
Given how the line from the external program's stderr stream manifests as shown in your question,
it sounds like you're running your code in the PowerShell ISE, which I suggest moving away from: The PowerShell ISE is obsolescent and should be avoided going forward (bottom section of the linked answer).
That the ISE surfaces stderr lines surface via PowerShell's error stream by default is especially problematic - see this GitHub issue.
The regular console doesn't do that, fortunately - it passes stderr lines through to the host (console), and prints them normally (not in red), which is the right thing to do, given that you cannot generally assume that all stderr output represents errors (the stream's name notwithstanding).
With well-behaved external programs, you should only ever derive success vs. failure from their process exit code (as reflected in the automatic $LASTEXITCODE
variable[1]), not from the presence of stderr output.: exit code 0
indicates success, any nonzero exit code (typically) indicates failure.
As for your specific case:
In the regular console, the value of the $ErrorActionPreference
preference variable does not apply to external programs such as schtasks.exe
, except in the form of a bug [fixed in PowerShell 7.2+] when you also use a 2>
redirection - see GitHub issue #4002; as of PowerShell 7.1.0-preview.6; the corrected behavior is a available as experimental feature PSNotApplyErrorActionToStderr
.
Since your schtasks /query /TN 'SOMETASK'
command functions as a test, you can do the following:
# Execute with all streams silenced (both stdout and stderr, in this case).
# schtask.exe will indicate the non-existence of the specified task
# with exit code 1
schtasks /query /TN 'SOMETASK' *>$null
if ($LASTEXITCODE -eq 0) { # success, task exists
"Processing removal of scheduled task`n"
# ...
}
# You can also squeeze it into a single conditional, using
# $(...), the subexpression operator.
if (0 -eq $(schtasks /query /TN 'SOMETASK' *>$null; $LASTEXITCODE)) { # success, task exists
"Processing removal of scheduled task`n"
# ...
}
In your specific case, a more concise solution is possible, which relies on your schtasks
command (a) producing stdout output in the case of success (if the task exists) and (b) only doings so in the success case:
# Execute with stderr silenced.
# Rely on the presence of stdout output in the success case only
# to make the conditional true.
if (schtasks /query /TN 'SOMETASK' 2>$null) { # success, task exists
"Processing removal of scheduled task`n"
# ...
}
If schtasks.exe
produces stdout output (which maps to PowerShell's success output stream, 1
), PowerShell's implicit to-Boolean conversion will consider the conditional $true
(see the bottom section of this answer for an overview of PowerShell's to-Boolean conversion rules).
Note that a conditional only ever acts on the success output stream's output (1
), other streams are passed through, such as the stderr output (2
) would be in this case (as you've experienced).
2>$null
silences stderr output, by redirecting it to the null device.
1
and 2
are the numbers of PowerShell's success output / error streams, respectively; in the case of external programs, they refers to their stdout (standard output) and stderr (standard error) streams, respectively - see about_Redirection
.
You can also capture stderr output with a 2>
redirection, if you want to report it later (or need to examine it specifically for an ill-behaved program that doesn't use exit codes properly).
2> stderr.txt
sends the stderr lines to file sdterr.txt
; unfortunately, there is currently no way to capture stderr in a variable - see GitHub issue #4332, which proposes syntax 2>&variableName
for that.
- As implied by the aforementioned bug, you must ensure that
$ErrorActionPreference
isn't set to 'Stop'
, because the 2>
will then mistakenly trigger a script-terminating error.
Aside from the aforementioned bug, using 2>
currently has another unexpected side effect [fixed in PowerShell 7.2+]: The stderr lines are unexpectedly also added to the automatic $Error
collection, as if they're errors (which they cannot assumed to be).
- The root cause of both issues is that stderr lines are unexpectedly routed via PowerShell's error stream, even though there is no good reason to do so - see GitHub issue #11133.
[1] Note that the automatic $?
variable that indicates success vs. failure as a Boolean ($true
/ $false
) is also set, but not reliably so: since stderr output is currently (v7.0) unexpectedly routed via PowerShell's error stream if redirected with 2>&
, the presence of any stderr output invariably sets $?
to $false
, even if the external program reports overall success, via $LASTEXITCODE
reporting 0
. Therefore, the only reliable way to test for success is $LASTEXITCODE -eq 0
, not $?
.