1

Hi guys, I want to create a function that will be checking execution of selected commands and in case of catched failure retry 3 times and display the command name with the retry message. I placed a test command "Test Connection" between the function brackets to be evaluted, retry seems to be working, however I can not get the command name to be printed. I tried with "$($MyInvocation.MyCommand.ScriptBlock)", but it prints out the whole function intead of that executed line that I'm checking. Can anyone please advice here? Thanks a lot in advance

function Execute-WithRetry([ScriptBlock]$command) {

    $Stoploop = $false
    $count = 0
 
    do {
        try {
            & $command
            Write-Host "Download completed"
            $Stoploop = $true
        }
        catch {
            if ($count -eq 3) {
               
                Write-Host "Could not download after 3 retries."
                $Stoploop = $true
            }
            else {
                Write-Host "Could not download the files retrying in 5 seconds..."
                Write-Host "Executing: $($MyInvocation.MyCommand.ScriptBlock)"
                Start-Sleep -Seconds 5
            }
        }
        $count++
    }
    While  ($Stoploop -eq $false)
    pause

    }


Execute-WithRetry {
  Test-Connection -ComputerName 192.10.129.15 -ErrorAction Stop
}
5
  • Sounds like you're looking for $MyInvocation.Line Commented Nov 3, 2023 at 15:47
  • it will only show a line number not a whole command, in my case "Test-Connection -...." Commented Nov 3, 2023 at 15:50
  • You are mistaken, the Line property contains a string - the line on which the statement that threw the error starts. Try grabbing the invocation info directly from the error record if $MyInvocation isn't giving you anything: try {Test-Connection nonexistingfqdn -ErrorAction Stop}catch {Write-Host "Failed at '$($_.InvocationInfo.Line |% Trim)'"} Commented Nov 3, 2023 at 15:53
  • It shows me the following: Failed at '& $command' Commented Nov 3, 2023 at 16:06
  • And $MyInvocation.Line on its own prints out me the name of the function "Execute-WithRetry" instead of the "Test-Connection" that is placed in the function. Commented Nov 3, 2023 at 16:13

2 Answers 2

3

As mentioned in the comments, if you want just the "offending" line from the script block, grab the invocation info from the error record inside the catch block (as opposed to $MyInvocation) and use the Line property to get the line on which the statement that failed starts:

function Invoke-Retry {
  param([scriptblock]$Command)

  $stop = $false
  $count = 0

  $ErrorActionPreference = 'Stop'

  do {
    try {
      &$Command

      Write-Host "Download completed"
      $stop = $true
    }
    catch {
      if ($count -ge 3) {
        Write-Host "Could not download after 3 retries."
        $stop = $true
      }
      else {
        Write-Host "Could not download the files, retrying in 5 seconds"
        Write-Host "Error occurred while executing: `n>> $($_.InvocationInfo.Line)" -ForegroundColor Red
        Start-Sleep -Seconds 5
      }
    }
    $count++
  } while (-not $stop)
  pause
}

Which should give you the actual line from the script block:

PS ~> Invoke-Retry {
  Test-Connection -ComputerName doesntexist
}
Could not download the files retrying in 5 seconds
Error occurred while executing:
>>   Test-Connection -ComputerName doesntexist

Could not download the files retrying in 5 seconds
Error occurred while executing:
>>   Test-Connection -ComputerName doesntexist

Could not download the files retrying in 5 seconds
Error occurred while executing:
>>   Test-Connection -ComputerName doesntexist

Could not download after 3 retries.
Press Enter to continue...:

Note: starting with version 7.4, the invocation info class also exposes the full extent of any multiline statements via the Statement property

Sign up to request clarification or add additional context in comments.

2 Comments

Nicely done (my answer took the question literally). Good to know about .InvocationInfo.Statement; as of PowerShell 7.4.0-rc.1, there's a (small) problem with it: see GitHub issue #20638.
@mklement0 Thanks for the heads up, looks like we need more tests ^_^
1

Preface:

  • This answer addresses the question as asked: how to obtain the entire source-code representation of the script block whose invocation triggered an error.

  • Mathias' helpful answer demonstrates how to extract the specific line inside the script block's source code that triggered the error.


It looks like you're looking for the source-code representation of the script block stored in $command.

Calling .ToString() on a script block returns just that (without the enclosing { and }) and, therefore, simply referencing a script block inside "...", an expandable string has the same effect.

Therefore, this should do what you want (the surrounding { and } are solely to give the full appearance of the original script-block literal):

Write-Host "Executing: {$command}"

Note:

  • The string representation is exactly what you place inside {...}, i.e. a script-block literal, and can therefore include newlines.

A simplified example:

# Sample function that prints the source code of a given script block.
function Foo { param([scriptblock] $Command) "Source code: {$Command}" }

# Sample call
# ->
#  "Source code: { Get-Date -Format 'yyyy' }" 
Foo -Command { Get-Date -Format 'yyyy' }

Calling .ToString() directly on a script-block literal makes the behavior obvious:

# -> " Get-Date -Format 'yyyy' "
{ Get-Date -Format 'yyyy' }.ToString()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.