1

Here is my testing script:

trap {"trapped"}

if ($true) {
  nonsenseString1
  exit 0
}

nonsenseString2

and the result is:

trapped
nonsenseString1 : The term 'nonsenseString1' is not recognized ...
trapped
nonsenseString2 : The term 'nonsenseString2' is not recognized ...

If this not what I wanted and what I expected. After it trapped nonsenseString1, it should exit 0, why it doesn't? This happens in both powershell 5 and 7.

I discovered this has to do with the if conditional block. If I take off the block, then it behaves as I expected.

PS: My real purpose was to record any errors that may happen on a Powershell script, and people tell me that I should use Start-Transcript:

How do I redirect stdout and stderr inside a powershell script?

Since I do not know when the error happens, I want to Stop-Transcript in a trap.

0

1 Answer 1

2

Selectively use a try / catch / finally statement to act on a terminating error caused by an individual statement (see also the bottom section):

$trapActions = { "trapped" }

trap { & $trapActions }

if ($true) {
  try {
    nonsenseString1
  } catch {
    $_ | Write-Error
    & $trapActions
    exit 1
  }
}

nonsenseString2

Note that I'm using exit 1, because by convention failure is signaled with a nonzero exit code.

Note that trap only acts on terminating errors (both statement and script-terminating ones), not also on the more typical non-terminating errors.

If it's acceptable to exit on the first terminating error - wherever it occurs - you can simply use trap { "trapped"; break } in your original code.


As for what you tried:

The problem is indeed that, when a trap statement is triggered, the enclosing block ({ ... }) is exited, so that the exit 0 never executes; instead, execution resumes with the first statement after the block, so that nonsenseString2 executes too.

This mirrors the behavior of the try { ... } catch { ... } finally { ... } statement, which was introduced later than trap, and is nowadays typically used in lieu of the latter.

That is, as soon as a terminating error occurs inside the try block, control is transferred to the catch block, with any remaining statements in the try block not getting executed.

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

9 Comments

Thanks for sharing that, @puravidaso; note that *> logfile alone would capture all streams; as for eliminating the first argument: you probably meant $args = if ($args.Count -gt 1) { $args[1..($args.Count-1)] }
@puravidaso, you want a range of array elements starting with the 2nd one (index 1), that's why you need .., not , - e.g., $args[1,3]extracts two individual elements, those with index 1 and 3. (Quick aside: $args.Length and $args.Count are equivalent). Yes, by default exceeding the upper bound of the array is quietly tolerated, but not with Set-StrictMode -Version 3 or higher in effect (not often used). The if statement is necessary to guard against the single-element case, because the range expression 1..0 would then result in indices 1 and 0.
If you're not worried about strict mode, you can therefore simplify to: $args = $args[1..$args.Count], which yields the empty array if only one argument was passed.
@puravidaso, use @args instead of $args to pass the arguments through (using splatting), which solves the problem.
This is like Unix shell's "$@" that I was looking for. Thanks!
|

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.