1

Is it possible to trace where output (to the console) is coming from in a Powershell script? I've got a script which is outputting information to me but I'm not sure which line is making the output. Is it possible to, for example, use Set-PSBreakpoint and tell it to break when info is returned to the console?

Cheers


I'm getting hundreds of "False"s returning on their own lines. Here's the part of the code where the output is coming from:

    $ar = Function-which_returns_array_of_objects
    $gr = Function-which_returns_array_of_objects

    Write-Host "To begin with..."
    Write-Host "$($ar.count) assets"
    Write-Host "$($gr.count) goods"

    foreach($asset in $ar){
        if(!(Test-NoNA -string $asset.Serial)){continue}

        #See if the particular serial number exists in Goods Received
        $found = @()
        $gr | Where {$_.SerialNumber -eq $asset.serial} | %{
            $found += $_
            # and then mark the entry as one that has to be deleted from GR
            $_.Delete = "YES"
        }
        if($found.count -eq 1){
            #Serial Number has been found once in GR
            #We want to check its PN...
            if(Test-NoNA -string $found.PartNumber -and $found.PartNumber -ne $asset.Model){
                #add it to the asset if its good and not the same as the model number...
                $asset.PartNumber -eq $found.PartNumber
            }
        }elseif(!$found -or $found.count -eq 0){
            #No entries found in GR
            #Shouldn't be the case but doesn't actually do any damage as we'd be deleting the GR entry anyway
        }elseif($found.count -gt 1){
            #More than one match for the SN - probably means a SN like "N/A" has got through the earlier checks
            Write-Warning "More than one match for SN: '$($asset.serial)'"
        }else{
            #Default catcher
            Write-Warning "Unknown Error for SN: '$($asset.serial)'"
        }
    }

Also, heres Test-NoNA:

function Test-NoNA($string){
#check that the given string is not blank, N/A, ?, etc. Returns true if string is good
if($string -and $string -ne "" -and $string -ne "N/A" -and $string -ne "NA" -and $string -ne '?' -and $string -isnot [System.DBNull]){return $true}
}
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • please post the output so we can make a wild guess as to what line of your still-secret code is triggering the stuff. of course, you _could_ try posting some code ... [*grin*] i understand that it may not be possible to post the whole script - but if you can, please do so. perhaps to Gist.GitHub? – Lee_Dailey Nov 22 '18 at 05:08
  • 1
    this line `$asset.PartNumber -eq $found.PartNumber` looks like it is part of the problem. your comment makes it seem that you intended the `-eq` to be `=`. [*grin*] – Lee_Dailey Nov 22 '18 at 06:32
  • Ahhh that's it! That explains some other issues with the script as well. – Will Maclean Nov 22 '18 at 21:39
  • kool! glad to know you got it fixed ... [*grin*] – Lee_Dailey Nov 23 '18 at 16:48

2 Answers2

2

Unfortunately,

  • Set-PSBreakpoint -Command works well with explicit cmdlet calls,

    • E.g., calls to Write-Output and Write-Host in script ./myscript.ps1 would cause breaking into the debugger with a prior Set-PSBreakpoint -Command Write-* ./myscript.ps1 call),
  • but does not work with implicit output, from statements whose output is neither captured nor redirected (e.g., 'foo', 1 + 2, Get-Date).

In the specific case at hand, statements such as $asset.PartNumber -eq $found.PartNumber caused the unwanted output, due to confusing the -eq operator (comparison) with the = operator (assignment), as diagnosed by Lee_Daily. -eq produces output (the Boolean result of a comparison), whereas = does not.

Workarounds:

  • Run Set-PSDebug -Trace 1 before calling your script, which prints each source-code line before the output it produces, if any. -Trace 2 provides additional details.

  • Use the following technique, which tells you the location and source code of each statement that produces success output, whether implicitly or explicitly:

    ./myscript.ps1 | % { Write-Verbose -vb "Output from $(($entry=(Get-PsCallStack)[1]).Location): $($entry.Position.Text)"; $_ }
    
    • The script's success output is processed object by object via ForEach-Object (%), producing a verbose message reporting the location (script path and line number) as well as the source code of the originating statement (obtained via Get-PSCallStack), before passing the object at hand ($_) through.
  • If you want to examine the script's runtime state on a given line, say 15:

    • Set-PSBreakpoint -Script ./myscript.ps1 -Line 15

    • Alternatively, in PSv5+, if modifying the script (temporarily) is an option, place a Wait-Debugger call in your script at the location where you'd like to break into the debugger.

  • Use the debugging capabilities of Visual Studio Code with the PowerShell extension to set breakpoints and/or step through the execution of your script statement by statement.

    • Alternatively, use Set-PSDebug -Step for the equivalent operation directly in a PowerShell window.
mklement0
  • 382,024
  • 64
  • 607
  • 775
0

Yes , try this. This should break whereever a write statement is first found.

  Set-PSBreakpoint -Script Sample.ps1 -Command "write*"

This command sets a breakpoint on every command in the Sample.ps1 script that begins with write, such as Write-Host. For more Refer docs

HariHaran
  • 3,642
  • 2
  • 16
  • 33
  • 1
    @Harihan thanks, but the output isn't coming from a "Write-*" style command - I think it's being returned from a function or operator. I'm after something which captures when output is generated, not when certain commands are called. – Will Maclean Nov 22 '18 at 04:59
  • Then you should post some sample code for better clarification. – HariHaran Nov 22 '18 at 05:00
  • I've edited the question to include some sample code – Will Maclean Nov 22 '18 at 05:15