Conditionally Redirecting Output from Inside a Powershell Script File

If you look at documentation and examples about output redirection in Powershell, you'll often find code showing you how to redirect e.g. Write-Warning on the command line level like shown here:

.\script.ps1 3> warnings.log

Usually, it's best practice to let the caller of a script decide how to apply output redirection. But there might be cases where letting a script itself decide might have some appeal. One example: Some of my scripts are not only used interactively but also being run as scheduled tasks. To be able to check possible warnings from such a script run after its completion I wanted to redirect the output of Write-Warning. The straightforward way to do this is to create a scheduled task definition including the command line arguments necessary for output redirection:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -File C:\scripts\get-allUserGroupMemberships.ps1 3> warnings.log

But this scheduled definition will redirect the warnings to the same file on every execution, thus overwriting the data from the last run. Overwriting could easily be avoided by using 3>> warnings.log to append to the now cumulative log file. But what if I wanted to have a separate log file for every (e.g. daily) execution of this script?

Following the documentation above and looking at the more advanced examples it became clear to me that output redirection in Powershell is not limited to passing command line arguments to a script being executed. Instead, every single code block or function call can be augmented with output redirection parameters.

Now how to apply output redirection on the code block or function call level to the use case I intended it for? A simple implementation would be a script with a parameter $WarnRedirect. If called without a value for this parameter (e.g. from a console session) the main body of the script would call its 'worker function' without redirection parameters. If there is a value for this parameter, output redirection will be applied:

[CmdletBinding()]
param(
  [string] $WarnRedirect = ""
)

function Write-A-Warning {
    [CmdletBinding()]
    param()
    Write-Warning "`nWarning message $(Get-Date)`n"
}

$cmd = "Write-A-Warning"
if ($WarnRedirect) {
        $WarningLogFile = "Warnings-" + $(Get-Date -Format "yyyyMMdd-HHmm") + ".log"
    $cmd += " 3>$WarningLogFile"
}
Invoke-Expression $cmd

If you want to avoid the use of Invoke-Expression the last few script lines (its main code block) could be also written as follows:

if ($WarnRedirect) {
        $WarningLogFile = "Warnings-" + $(Get-Date -Format "yyyyMMdd-HHmm") + ".log"
        Write-A-Warning 3>$WarningLogFile
} else {
    Write-A-Warning
}

Of course, there are other ways of achieving the same result of getting one warning file per scheduled script execution. One such implementation would be to create a parameterless 'wrapper script' and use this in the definition of the scheduled task. The wrapper script could then construct and execute a Powershell command line for the 'worker script' with the appropriate redirection arguments, including a date-dependent output file name. But my own tastes tend to try to avoid a proliferation of script files, thus opting for the approach presented here.

0
Subscribe to my newsletter

Read articles from Marcus Schommler directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Marcus Schommler
Marcus Schommler

Started as a software developer specialized in UI and database design, later moved into IT systems administration. Working mainly with infrastructure and services based on Windows Server (Exchange 2016, Active Directory, WSUS, MDT, ...) and VMWare vSphere.