Pxtl
Pxtl

Reputation: 964

Capture verbose stream without enabling -Verbose

Maybe there's no way to do this in PowerShell.

If I have the file verboseTest.ps1

[CmdletBinding()]    # CmdletBinding attribute enables -verbose flag
Param()
Write-Verbose 'verbose test'

If I call .\verboseTest.ps1, I see nothing as expected.

If I call .\verboseTest.ps1 -Verbose I see the output.

VERBOSE: verbose test outer

as expected.

But if I call .\verboseTest.ps1 4>&1 I don't. The verbose stream is lost.

Now, Info behaves completely differently.

I have the file infoTest.ps1 and it behaves sanely.

[CmdletBinding()]    # Add CmdletBinding attribute
Param()
Write-Information 'info test outer'

If I call .\infoTest.ps1, I see nothing as expected.

If I call .\infoTest.ps1 -InformationAction 'Continue', which is analogous to the -Verbose flag, it succeeds and outputs info test outer to the console.

And if I call .\infoTest.ps1 6>&1 it also outputs info test outer to the console! So somehow the "information" stream behaves completely differently from the "verbose" stream.

The behavior of the info stream makes sense. The write-information commands write to the stream, and I can redirect it or view it or not as I see fit. The "verbose" does not! I have to enable showing it in the output in order to redirect it? Am I doing something wrong? How does this make sense?

Do I have to enable verbose preference in order to redirect the output? And if so, can I do that inside the method or does it change it globally session-wide?

Upvotes: 2

Views: 595

Answers (1)

mklement0
mklement0

Reputation: 437383

Unfortunately, the verbose stream (stream number 4) isn't just silent when not explicitly turned on, but no data is written to it.

That is, Write-Verbose statements are effective no-ops[1], unless -Verbose is passed or preference variable $VerbosePreference is set to Continue.


Among PowerShell's silent-by-default output streams - verbose (4), debug (5) and information (6) - the information stream is the lone exception: Write-Information statements always write to it, whether it happens to be silenced or not.


Setting preference variable $VerbosePreference to Continue turns on verbose output for all commands in the current scope and any child scopes - but there's an important exception:

Advanced functions implemented in modules see only a global instance of $VerbosePreference when called from a script; by contrast, binary cmdlets are not affected. This highly problematic behavior is discussed in this GitHub issue.

You can work around that problem as follows:

# Create a script-local copy of the global parameter-defaults
# hashtable.
# Note: If you want to clear the global presets, call .Clear()
#       after cloning.
$PSDefaultParameterValues = $PSDefaultParameterValues.Clone()

# Preset the -Verbose switch for all commands that support it.
$PSDefaultParameterValues['*:Verbose'] = $true

# ... call commands, which will behave as if -Verbose had been passed.

[1] However, the cmdlet is still called, which means that arguments you pass to it are evaluated, so it is hypothetically still possible for a silent Write-Verbose call to have side effects, such as when passing an expandable string as the message that contains a subexpression ($(...)) with side effects.

Upvotes: 4

Related Questions