Skip to main content
added 218 characters in body
Source Link
mklement0
  • 452.5k
  • 68
  • 728
  • 988
# To create an *alias* for this function that conforms to PowerShell's naming # standard, use, e.g.: # Set-Alias iwe Invoke-WithErrorHandling. function Invoke-WithErrorHandling { # Parse the arguments into the executable and the pass-through arguments # for it. $exe, $argsForExe = $Args # Workaround for v7.2-: # Prevents 2> redirections applied to calls to this function # from accidentally triggering a terminating error in PS v7.2- # See bug report at https://github.com/PowerShell/PowerShell/issues/4002 $ErrorActionPreference = 'Continue' # In v7.4+, prevent PowerShell itself from emitting an error in response # to a nonzero exit code reported from a program. $PSNativeCommandUseErrorActionPreference = $false try { if ($MyInvocation.ExpectingInput) { # Relay pipeline input. Note: The input is collected first, in full, # up front, before it is relayed to the target executable. # Also, the v7.4+ ability to pipe raw bytes between two external # executables is unavailable due to using this wrapper function. $input | & $exe $argsForExe } else { & $exe $argsForExe } } catch { throw } # catch is triggered ONLY if $exe can't be found, never for errors reported by $exe itself if ($LASTEXITCODE) { throw "`"$exe`" indicated failure (exit code $LASTEXITCODE)." } } 
  • The following function therefore expects a single, mandatory script-block argument containing the command to execute (and optionally also pipeline input), so that in order to make it execute git foo, you'd call it as
    Invoke-WithErrorHandling { git foo }
# To create an *alias* for this function that conforms to PowerShell's naming # conventions, use, e.g.: # Set-Alias iwe Invoke-WithErrorHandling. function Invoke-WithErrorHandling { param( [Parameter(Mandatory, Position=0)] [scriptblock] $ScriptBlock, [Parameter(ValueFromPipeline)] # Optional pipeline input $InputObject ) begin { # Set up a steppable pipeline.   $steppablePipeline = $ScriptBlock.GetSteppablePipeline($myInvocation.CommandOrigin)  $steppablePipeline.Begin($PSCmdlet)   # Workaround for v7.2-:   # Prevents 2> redirections applied to calls to this function   # from accidentally triggering a terminating error in PS v7.2-   # See bug report at https://github.com/PowerShell/PowerShell/issues/4002   $ErrorActionPreference = 'Continue'   # In v7.4+, prevent PowerShell itself from emitting an error in response   # to a nonzero exit code reported from a program.   $PSNativeCommandUseErrorActionPreference = $false     try {}     if ($MyInvocation.ExpectingInput)process {   # Relay pipelinePass input.the Note:current Thepipeline input is collected first, in full, # up front, before it is relayed to the target executablethrough.   # Also, the v7.4+ ability to pipe raw bytes between twotry external{   # executables is unavailable due to using this wrapper function$steppablePipeline. $input | & $ScriptBlockProcess($_)   } else {   & $ScriptBlock } } catch { throw } # catch is triggered   ONLY if $exe can't}  be found,end never{  for errors reported by$steppablePipeline.End()  $exe itself  if ($LASTEXITCODE) { throw "`"$ScriptBlock`" indicated failure (exit code $LASTEXITCODE)." }  } } 
  • Unlike the earlier function, this one is an advanced one, which is unproblematic, because the one and only argument specifying the command line to execute cannot collide with common parameters anymore. However, common parameters such as -OutVariable and -ErrorVariable do not function properly, due to the design limitation discussed in GitHub issue #5758

  • The notes on the earlier function apply to this one too, except that pipeline input is supported in a streaming fashion by this function (no need to collect all input up front first), by virtue of it being implemented as a proxy (wrapper) function - see this answer for additional information.

# To create an *alias* for this function that conforms to PowerShell's naming # standard, use, e.g.: # Set-Alias iwe Invoke-WithErrorHandling. function Invoke-WithErrorHandling { # Parse the arguments into the executable and the pass-through arguments # for it. $exe, $argsForExe = $Args # Workaround for v7.2-: # Prevents 2> redirections applied to calls to this function # from accidentally triggering a terminating error in PS v7.2- # See bug report at https://github.com/PowerShell/PowerShell/issues/4002 $ErrorActionPreference = 'Continue' # In v7.4+, prevent PowerShell itself from emitting an error in response # to a nonzero exit code reported from a program. $PSNativeCommandUseErrorActionPreference = $false try { if ($MyInvocation.ExpectingInput) { # Relay pipeline input. Note: The input is collected first, in full, # up front, before it is relayed to the target executable. # Also, the v7.4+ ability to pipe raw bytes between two external # executables is unavailable due to using this wrapper function. $input | & $exe $argsForExe } else { & $exe $argsForExe } } catch { throw } # catch is triggered ONLY if $exe can't be found, never for errors reported by $exe itself if ($LASTEXITCODE) { throw "`"$exe`" indicated failure (exit code $LASTEXITCODE)." } } 
  • The following function therefore expects a single, mandatory script-block argument containing the command to execute, so that in order to make it execute git foo, you'd call it as
    Invoke-WithErrorHandling { git foo }
# To create an *alias* for this function that conforms to PowerShell's naming # conventions, use, e.g.: # Set-Alias iwe Invoke-WithErrorHandling. function Invoke-WithErrorHandling { param( [Parameter(Mandatory)] [scriptblock] $ScriptBlock ) # Workaround for v7.2-: # Prevents 2> redirections applied to calls to this function # from accidentally triggering a terminating error in PS v7.2- # See bug report at https://github.com/PowerShell/PowerShell/issues/4002 $ErrorActionPreference = 'Continue' # In v7.4+, prevent PowerShell itself from emitting an error in response # to a nonzero exit code reported from a program. $PSNativeCommandUseErrorActionPreference = $false     try {   if ($MyInvocation.ExpectingInput) {   # Relay pipeline input. Note: The input is collected first, in full, # up front, before it is relayed to the target executable.   # Also, the v7.4+ ability to pipe raw bytes between two external   # executables is unavailable due to using this wrapper function. $input | & $ScriptBlock   } else {   & $ScriptBlock } } catch { throw } # catch is triggered ONLY if $exe can't be found, never for errors reported by $exe itself  if ($LASTEXITCODE) { throw "`"$ScriptBlock`" indicated failure (exit code $LASTEXITCODE)." } } 
  • Unlike the earlier function, this one is an advanced one, which is unproblematic, because the one and only argument specifying the command line to execute cannot collide with common parameters anymore. However, common parameters such as -OutVariable and -ErrorVariable do not function properly, due to the design limitation discussed in GitHub issue #5758

  • The notes on the earlier function apply to this one too.

# To create an *alias* for this function that conforms to PowerShell's naming # standard, use, e.g.: # Set-Alias iwe Invoke-WithErrorHandling function Invoke-WithErrorHandling { # Parse the arguments into the executable and the pass-through arguments # for it. $exe, $argsForExe = $Args # Workaround for v7.2-: # Prevents 2> redirections applied to calls to this function # from accidentally triggering a terminating error in PS v7.2- # See bug report at https://github.com/PowerShell/PowerShell/issues/4002 $ErrorActionPreference = 'Continue' # In v7.4+, prevent PowerShell itself from emitting an error in response # to a nonzero exit code reported from a program. $PSNativeCommandUseErrorActionPreference = $false try { if ($MyInvocation.ExpectingInput) { # Relay pipeline input. Note: The input is collected first, in full, # up front, before it is relayed to the target executable. # Also, the v7.4+ ability to pipe raw bytes between two external # executables is unavailable due to using this wrapper function. $input | & $exe $argsForExe } else { & $exe $argsForExe } } catch { throw } # catch is triggered ONLY if $exe can't be found, never for errors reported by $exe itself if ($LASTEXITCODE) { throw "`"$exe`" indicated failure (exit code $LASTEXITCODE)." } } 
  • The following function therefore expects a single, mandatory script-block argument containing the command to execute (and optionally also pipeline input), so that in order to make it execute git foo, you'd call it as
    Invoke-WithErrorHandling { git foo }
# To create an *alias* for this function that conforms to PowerShell's naming # conventions, use, e.g.: # Set-Alias iwe Invoke-WithErrorHandling function Invoke-WithErrorHandling { param( [Parameter(Mandatory, Position=0)] [scriptblock] $ScriptBlock, [Parameter(ValueFromPipeline)] # Optional pipeline input $InputObject ) begin { # Set up a steppable pipeline.   $steppablePipeline = $ScriptBlock.GetSteppablePipeline($myInvocation.CommandOrigin)  $steppablePipeline.Begin($PSCmdlet)   # Workaround for v7.2-:   # Prevents 2> redirections applied to calls to this function   # from accidentally triggering a terminating error in PS v7.2-   # See bug report at https://github.com/PowerShell/PowerShell/issues/4002   $ErrorActionPreference = 'Continue'   # In v7.4+, prevent PowerShell itself from emitting an error in response   # to a nonzero exit code reported from a program.   $PSNativeCommandUseErrorActionPreference = $false }    process { # Pass the current pipeline input through. try { $steppablePipeline.Process($_) } catch { throw }   }  end {  $steppablePipeline.End()  if ($LASTEXITCODE) { throw "`"$ScriptBlock`" indicated failure (exit code $LASTEXITCODE)." }  } } 
  • Unlike the earlier function, this one is an advanced one, which is unproblematic, because the one and only argument specifying the command line to execute cannot collide with common parameters anymore. However, common parameters such as -OutVariable and -ErrorVariable do not function properly, due to the design limitation discussed in GitHub issue #5758

  • The notes on the earlier function apply to this one too, except that pipeline input is supported in a streaming fashion by this function (no need to collect all input up front first), by virtue of it being implemented as a proxy (wrapper) function - see this answer for additional information.

added 254 characters in body
Source Link
mklement0
  • 452.5k
  • 68
  • 728
  • 988

Alternatively, if you want to pass the command line to execute as a single argument, do not use a string, but a script block (type [scriptblock], literal form { ... }):

Note:

  • The following function therefore expects a single, mandatory script-block argument containing the command to execute, so that in order to make it execute git foo, you'd call it as
    Invoke-WithErrorHandling { git foo }
# To create an *alias* for this function that conforms to PowerShell's naming # conventions, use, e.g.: # Set-Alias iwe Invoke-WithErrorHandling. function Invoke-WithErrorHandling { param( [Parameter(Mandatory)] [scriptblock] $ScriptBlock ) # Workaround for v7.2-: # Prevents 2> redirections applied to calls to this function # from accidentally triggering a terminating error in PS v7.2- # See bug report at https://github.com/PowerShell/PowerShell/issues/4002 $ErrorActionPreference = 'Continue' # In v7.4+, prevent PowerShell itself from emitting an error in response # to a nonzero exit code reported from a program. $PSNativeCommandUseErrorActionPreference = $false try { if ($MyInvocation.ExpectingInput) { # Relay pipeline input. Note: The input is collected first, in full, # up front, before it is relayed to the target executable. # Also, the v7.4+ ability to pipe raw bytes between two external # executables is unavailable due to using this wrapper function. $input | & $ScriptBlock } else { & $ScriptBlock } } catch { throw } # catch is triggered ONLY if $exe can't be found, never for errors reported by $exe itself if ($LASTEXITCODE) { throw "`"$ScriptBlock`" indicated failure (exit code $LASTEXITCODE)." } } 

Note:

  • Unlike the earlier function, this one is an advanced one, which is unproblematic, because the one and only argument specifying the command line to execute cannot collide with common parameters anymore. However, common parameters such as -OutVariable and -ErrorVariable do not function properly, due to the design limitation discussed in GitHub issue #5758

  • The notes on the earlier function apply to this one too.

Alternatively, if you want to pass the command line to execute as a single argument, do not use a string, but a script block (type [scriptblock], literal form { ... }):

Note:

  • The following function therefore expects a single, mandatory script-block argument containing the command to execute, so that in order to make it execute git foo, you'd call it as
    Invoke-WithErrorHandling { git foo }
# To create an *alias* for this function that conforms to PowerShell's naming # conventions, use, e.g.: # Set-Alias iwe Invoke-WithErrorHandling. function Invoke-WithErrorHandling { param( [Parameter(Mandatory)] [scriptblock] $ScriptBlock ) # Workaround for v7.2-: # Prevents 2> redirections applied to calls to this function # from accidentally triggering a terminating error in PS v7.2- # See bug report at https://github.com/PowerShell/PowerShell/issues/4002 $ErrorActionPreference = 'Continue' # In v7.4+, prevent PowerShell itself from emitting an error in response # to a nonzero exit code reported from a program. $PSNativeCommandUseErrorActionPreference = $false try { if ($MyInvocation.ExpectingInput) { # Relay pipeline input. Note: The input is collected first, in full, # up front, before it is relayed to the target executable. # Also, the v7.4+ ability to pipe raw bytes between two external # executables is unavailable due to using this wrapper function. $input | & $ScriptBlock } else { & $ScriptBlock } } catch { throw } # catch is triggered ONLY if $exe can't be found, never for errors reported by $exe itself if ($LASTEXITCODE) { throw "`"$ScriptBlock`" indicated failure (exit code $LASTEXITCODE)." } } 

Note:

  • Unlike the earlier function, this one is an advanced one, which is unproblematic, because the one and only argument specifying the command line to execute cannot collide with common parameters anymore. However, common parameters such as -OutVariable and -ErrorVariable do not function properly, due to the design limitation discussed in GitHub issue #5758

  • The notes on the earlier function apply to this one too.

added 254 characters in body
Source Link
mklement0
  • 452.5k
  • 68
  • 728
  • 988

The following avoids the common-parameter problemfollowing avoids the common-parameter problem that affects Santiago's solution, by defining the function as a simple (non-advanced) one that passes all arguments through:

Note:

  • As Santiago's solution, the function below is meant to be used with individual arguments that mimic direct-invocation syntax, e.g., to make the function execute git foo, invoke it as
    Invoke-WithErrorHandling git foo
  • As requested, the above creates the equivalent of a .NET exception, which is a fatal-by-default script-terminating error, via the throw keyword.

  • The function also supports relaying pipeline input to the target executable, albeit - of necessity - such input is collected in memory first, in full.

    • The only way to avoid this would be to use an advanced function, which is not an option, because it would re-introduce the common-parameters problem.

    • Also, the use of a PowerShell wrapper function invariably precludes the in PowerShell 7.4+ ability to pipe raw binary data between to external programs (see the bottom section of this answer for details).

  • There is still one remaining difference to direct invocation of external programs that cannot be avoided, whether or not the function is advanced.

    • Unquoted arguments containing , (e.g., foo,bar) must be quoted (e.g, 'foo,bar') in order to be passed through correctly; without quoting, the wrapper function receives the ,-separated tokens as separate arguments.
  • The above functionality is also available via the Native module's iee function (authored by me):

    • iee additionally compensates for PowerShell's long-standing bug with respect to passing arguments with embedded " characters in Windows PowerShell (whose latest and last version is 5.1.x), which was finally fixed - with selective exceptions - in PowerShell (Core) 7 v7.3+ - see this answer for details.
 

The following avoids the common-parameter problem that affects Santiago's solution, by defining the function as a simple (non-advanced) one that passes all arguments through:

  • As requested, the above creates the equivalent of a .NET exception, which is a fatal-by-default script-terminating error, via the throw keyword.

  • The function also supports relaying pipeline input to the target executable, albeit - of necessity - such input is collected in memory first, in full.

    • The only way to avoid this would be to use an advanced function, which is not an option, because it would re-introduce the common-parameters problem.

    • Also, the use of a PowerShell wrapper function invariably precludes the in PowerShell 7.4+ ability to pipe raw binary data between to external programs (see the bottom section of this answer for details).

  • There is still one remaining difference to direct invocation of external programs that cannot be avoided, whether or not the function is advanced.

    • Unquoted arguments containing , (e.g., foo,bar) must be quoted (e.g, 'foo,bar') in order to be passed through correctly; without quoting, the wrapper function receives the ,-separated tokens as separate arguments.
  • The above functionality is also available via the Native module's iee function (authored by me):

    • iee additionally compensates for PowerShell's long-standing bug with respect to passing arguments with embedded " characters in Windows PowerShell (whose latest and last version is 5.1.x), which was finally fixed - with selective exceptions - in PowerShell (Core) 7 v7.3+ - see this answer for details.

The following avoids the common-parameter problem that affects Santiago's solution, by defining the function as a simple (non-advanced) one that passes all arguments through:

Note:

  • As Santiago's solution, the function below is meant to be used with individual arguments that mimic direct-invocation syntax, e.g., to make the function execute git foo, invoke it as
    Invoke-WithErrorHandling git foo
  • As requested, the above creates the equivalent of a .NET exception, which is a fatal-by-default script-terminating error, via the throw keyword.

  • The function also supports relaying pipeline input to the target executable, albeit - of necessity - such input is collected in memory first, in full.

    • The only way to avoid this would be to use an advanced function, which is not an option, because it would re-introduce the common-parameters problem.

    • Also, the use of a PowerShell wrapper function invariably precludes the PowerShell 7.4+ ability to pipe raw binary data between to external programs (see the bottom section of this answer for details).

  • There is still one remaining difference to direct invocation of external programs that cannot be avoided, whether or not the function is advanced.

    • Unquoted arguments containing , (e.g., foo,bar) must be quoted (e.g, 'foo,bar') in order to be passed through correctly; without quoting, the wrapper function receives the ,-separated tokens as separate arguments.
  • The above functionality is also available via the Native module's iee function (authored by me):

    • iee additionally compensates for PowerShell's long-standing bug with respect to passing arguments with embedded " characters in Windows PowerShell (whose latest and last version is 5.1.x), which was finally fixed - with selective exceptions - in PowerShell (Core) 7 v7.3+ - see this answer for details.
 
Source Link
mklement0
  • 452.5k
  • 68
  • 728
  • 988
Loading