You are viewing this site over HTTP, which is not secure. Please consider switching to HTTPS instead.

Getting the Verbose switch setting in PowerShell

Thursday, September 22, 2016

I've been struggling to find a reliable, portable way to find out if my PowerShell scripts were called with the -Verbose switch. The solutions I found so far are all fairly convoluted, and don't work well if the verbose switch is passed as -Verbose:$false or -Verbose:$true. This morning, it finally hit me.

But first let me explain what I needed.

Say I have a script called SomeScript.ps1. It starts like this:

[CmdletBinding()]
Param()

$Verbose = Get-Verbose

 If called like

.\SomeScript.ps1

the $Verbose variable should be $false; if called with the -Verbose switch, like this:

.\SomeScript.ps1 -Verbose

the $Verbose variable should be $true.

Sounds simple enough, doesn't it? It is, actually. The solution hinges on writing to the Verbose output stream and catching the output - without showing it. If the output is non-empty, we're in Verbose mode.

  1. Start by writing a dummy text to the Verbose output stream:
    Write-Verbose "TEST"
    This will either show up, or not, depending on the -Verbose setting

  2. Now catch the output into a variable:
    Write-Verbose "TEST" -OutVariable output
    Doesn't work: $output is always empty, since the text appears in the Verbose output and we only capture the standard output steam into $output

  3. So we need to redirect the Verbose output to the standard output stream:
    Write-Verbose "TEST" -OutVariable output 4>&1
    Close! Using the magical formula '4>&1' we tell PowerShell to merge everything written to the standard output stream (e.g. Write-Output) with everything written to the Verbose stream. Standard out0put has number 1, verbose output is number 4, and the ampersand means "merge".

    In Verbose mode, the output variable now contains a single line "TEST"; otherwise, it contains no lines. (The output variable turns out to be an ArrayList, by the way).

    Problem: in Verbose mode, the string "TEST" is also written to standard-output - as we requested.

  4. The final step, therefore, is to redirect the output of the whole thing to the trash, e.g. dump it to Out-Null:
    (Write-Verbose "TEST" -OutVariable output 4>&1) | Out-Null
  5. Great success! In Verbose mode, $output is "TEST", otherwise $output is empty. All we have to do is turn it into a Boolean for consumption by callers:
    return (!!$output)
    (Note: $output is an ArrayList, so if ($output) ... will test if it has any elements. Returning $output will return the arraylist itself, which is not what we want. (!$output) is the wrong way around, so (!!$output) is what we need.)

There you have it! We need to make sure, of course, that Get-Verbose.ps1 is itself a nice Cmdlet, to make it recognize the -Verbose switch. So we need a [CmdletBinding()], which makes the entire script:

<#
    Get-Verbose - determine if the Cmdlet flag -Verbose is set.
#>
[Cmdletbinding()]
Param()

# Write the string "TEST" to the verbose output stream,
# but redirect that to standard output using 4>&1.
# Discard the output using Out-Null, but capture it in a variable
# using -OutVariable (an ArrayList!)
(Write-Verbose "TEST" -OutVariable output 4>&1) | Out-Null

# If Verbose is $true, the output ArrayList will contain data;
# if not, it will be empty
return (!!$output)

 The script does require Powershell 3.0, because we need the &-operator to redirect output, which is "reserved for future use" in PowerShell 1.0 and 2.0. I guess this is "future use" ;-)

Update, May 22nd, 2018: The script can, of course, be turned into a one-liner:

$verbose = (!!(Write-Verbose "" 4>&1))

No script, no intermediate output variable!