Search code examples
windowspowershellshellwindows-10trace

How to run a PowerShell script with verbose output?


I'm wondering if there's a way to run a PowerShell script such that both the commands and the output of each line of the script are printed. For example, in Bash you would write bash -x myscript or place a set -x at the top of your script. In Batch, you would omit the @echo off traditionally left at the top of your script. Does PowerShell have an equivalent of these constructs?

Things I've tried: Running powershell -? | sls verbose, which turned up nothing.


Solution

  • Just goes to show, @JamesKo, if you ask the wrong question you get the wrong answer :-(. Several people put forth good-faith answers here based on (a) lack of Linux exposure and (b) your use of the term verbose. In the following I will walk you through how Linux relates to PowerShell on this topic, but feel free to jump to the answer at the end if you are in a hurry. :-)

    Background

    In PowerShell, verbose has a very specific meaning which the PowerShell man page is even rather vague about:

    Displays detailed information about the operation performed by the command. This information resembles the information in a trace or in a transaction log. This parameter works only when the command generates a verbose message.

    It even sounds like what you want... but let's compare that to the Linux documentation for set -x which, depending on your flavor of Linux, could be this (from man-pages project)...

    The shell shall write to standard error a trace for each command after it expands the command and before it executes it.

    or this (from gnu)...

    Print a trace of simple commands, for commands, case commands, select commands, and arithmetic for commands and their arguments or associated word lists after they are expanded and before they are executed.

    The very first line of your question clearly and concisely agrees with these. But verbose in PowerShell is different. In a nutshell, turning on verbose mode (be it with the -Verbose command line switch or the $VerbosePreference variable) simply enables output from the verbose stream to the console. (Just like Linux offers two streams, stdout and stderr, PowerShell offers multiple streams: output stream, error stream, warning stream, verbose stream, and debug stream. You work with these streams in an identical fashion to that of Linux--you can even use, e.g., commands 4>&1 to merge the verbose stream to stdout, for example. (You can read more about PowerShell's multiple output streams in the Basic Writing Streams section of PowerShell One-Liners: Accessing, Handling and Writing Data and a good quick reference is the Complete Guide to PowerShell Punctuation.)

    The Answer

    The Set-PSDebug command will give you bash-equivalent tracing. You can even adjust the tracing detail with the -Trace parameter. First, here's the control, before using Set-PSDebug:

    PS> Get-PSDepth
    0
    

    With a value of 1 you get each line of code as it executes, e.g.:

    PS> Set-PSDebug -Trace 1
    PS> Get-PSDepth
    DEBUG:    1+  >>>> Get-PSDepth
    DEBUG:  141+  >>>> {
    DEBUG:  142+   >>>> $nest = -1
    DEBUG:  143+   >>>> $thisId = $pid
    DEBUG:  144+  while ( >>>> (ps -id $thisId).Name -eq 'powershell') {
    DEBUG:  145+    >>>> $thisId = (gwmi win32_process -Filter "processid='$thisId'").ParentProcessId
    DEBUG:  146+    >>>> $nest++
    DEBUG:  144+  while ( >>>> (ps -id $thisId).Name -eq 'powershell') {
    DEBUG:  148+   >>>> $nest
    0
    DEBUG:  149+  >>>> }
    

    With a value of 2 you also get variable assignments and code paths:

    PS> Set-PSDebug -Trace 2
    PS> Get-PSDepth
    DEBUG:    1+  >>>> Get-PSDepth
    DEBUG:     ! CALL function '<ScriptBlock>'
    DEBUG:  141+  >>>> {
    DEBUG:     ! CALL function 'Get-PSDepth'  (defined in file 'C:\Users\msorens\Documents\WindowsPowerShell\profile.ps1')
    DEBUG:  142+   >>>> $nest = -1
    DEBUG:     ! SET $nest = '-1'.
    DEBUG:  143+   >>>> $thisId = $pid
    DEBUG:     ! SET $thisId = '9872'.
    DEBUG:  144+  while ( >>>> (ps -id $thisId).Name -eq 'powershell') {
    DEBUG:  145+    >>>> $thisId = (gwmi win32_process -Filter "processid='$thisId'").ParentProcessId
    DEBUG:     ! SET $thisId = '10548'.
    DEBUG:  146+    >>>> $nest++
    DEBUG:     ! SET $nest = '0'.
    DEBUG:  144+  while ( >>>> (ps -id $thisId).Name -eq 'powershell') {
    DEBUG:  148+   >>>> $nest
    0
    DEBUG:  149+  >>>> }
    

    Those are traces of a simple cmdlet I wrote called Get-PSDepth. It prints the commands, assignments, etc. with the DEBUG prefix, intermixed with the actual output, which in this case is the single line containing just 0.