From 717787c26ff50d5e9130c34aadc5c4d5b9c86b64 Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Tue, 9 Feb 2021 07:59:37 -0800 Subject: [PATCH 01/16] Added Native Command Error Handling --- .../RFC00XX-Native-Command-Error-Handling.md | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 1-Draft/RFC00XX-Native-Command-Error-Handling.md diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md new file mode 100644 index 000000000..2e8bd8b9c --- /dev/null +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -0,0 +1,207 @@ +--- +RFC: RFC00XX +Author: Jason Helmick +Status: Draft +Area: Core +Comments Due: 10/31/2020 +--- + +# Native Command Error Handling + +PowerShell scripts using native commands would benefit from being able to use error handling +features like those used by cmdlets. + +## Motivation + +In PowerShell by default, script processing continues when non-terminating errors occur. This is a +benefit when expecting non-terminating errors in normal execution such as non-responsive computers +from a list. This default behavior is controlled with the preference variable +`$ErrorActionPreference` default of `Continue`. + +In production, often customers prefer that script execution stops when a non-terminating error +occurs. This is particularly true in CI where the preference is to fail fast. PowerShell currently +supports customers with this ability by setting the `$ErrorActionPreference` variable in the script +to `Stop`. + +```Powershell +$ErrorActionPreference = 'Stop' +``` + +Native commands usually return an exit code to the calling application which will be zero for +success or non-zero for failure. However, native commands currently do not participate in the +PowerShell error stream. Redirected `stderr` output is not interpreted the same as the PowerShell +error stream as many native commands use `stderr` as information/verbose stream and thus only the +exit code matters. Users working with native commands in their scripts will need to check the +execution status after each call using a helper function similar to below: + +```Powershell +if ($LASTEXITCODE -ne 0) +{ + throw "Command failed. See above errors for details" +} + ``` + +Simply relaying the errors through the error stream isn't the solution. The example itself doesn't +support all cases as `$?` can be false from a cmdlet or function error, making `$LASTEXITCODE` +stale. + +In POSIX shells, this need to terminate on command error is addressed by the `set -e` configuration, +which causes the shell to exit when a command fails. In addition, to ensure that an error is +returned if any command in a pipeline fails, POSIX shells address this need with `set -o pipefail` +configuration. + +This specification proposes a similar idea, but adapted to the PowerShell conventions of preference +variables and catchable, self-describing, terminating error objects. This proposal adds the +equivalent functionality of `set -eo pipefail` to return an error if any command in a pipeline +fails. + + The specification and alternative proposals are based on the + [Equivalent of bash `set -e` #3415](https://github.com/PowerShell/PowerShell/issues/3415) + committee review of the associated + [pull request](https://github.com/PowerShell/PowerShell/pull/3523), and + [implementation plan](https://github.com/PowerShell/PowerShell-RFC/pull/88#issuecomment-613653678) + +## Specification + +This RFC proposes a preference variable to configure the elevation of errors produced by native +commands to first-class PowerShell errors, so that native command failures will produce error +objects that are added to the error stream and may terminate execution of the script without added +boilerplate. + +The specification proposes similar functionality to the common POSIX shell configuration `set -eo pipefail`. + +- `set -e` - instructs to immediately exit if any command has a non-zero exit status. +- `set -o pipefail` - prevents errors in the pipeline from being masked. The return code for the + non-zero error is returned for the entire pipeline. + +```powershell + +Cat ./Nofile | head -n 1 +Cat: ./Nofile: No such file or directory +echo $? + + +$ErrorActionPreference = 'Stop' # This is equivalent to Set -e + +Cat ./Nofile | head -n 1 +Cat: ./Nofile: No such file or directory +echo $? + + +$PSNativeCommandErrorAction ='Stop' # Equivalent to Set -o pipefail +cat ./nofile | head -n 1 +Cat: ./Nofile: No such file or directory +echo $? + + +``` + +> [!NOTE] A common configuration command for POSIX shells `set -euo pipefail` includes the `set -u` +> configuration which returns an error if any variable has not been previously defined. This is +> equivalent to the existing PowerShell `Set-StrictMode` and is not addressed in this RFC. + +The `$PSNativeCommandErrorAction` preference variable will implement a version of the +`$ErrorActionPreference` variable for native commands. + +- The value will default to `Ignore` for compatibility with existing behavior. +- For non-zero exit codes and except for the value `Ignore`, an `ErrorRecord` will be added to + `$Error` that wraps the exit code and the command executed that returned the exit code. +- Initially, only the existing values of `$ErrorActionPreference` will be supported in + `$PSNativeCommandErrorAction` as described in the table below. + +### Valid values for `$PSNativeCommandErrorAction` + +| Value | Definition +---------------- | ------------------- +| Break | Enter the debugger when an error occurs or when an exception is raised. +| Continue | (Default) - Displays the error message and continues executing. +| Ignore | Suppresses the error message and continues to execute the command. +| Inquire | Displays the error message and asks you whether you want to continue. +| SilentlyContinue| No effect. The error message isn't displayed and execution continues without interruption. +| Stop | Displays the error message and stops executing. In addition to the error generated, the Stop value generates an ActionPreferenceStopException object to the error stream. stream +| Suspend | Automatically suspends a workflow job to allow for further investigation. + +### Preference variable resolves error handling conflicts + +In cases where an existing script already handles non-zero native command errors, the preference +variable `$PSNativeCommandErrorAction` may be set to `Ignore`. Error handling behavior of the +PowerShell cmdlets is handled separately with `$ErrorActionPreference`. + +### Error object + +The reported error record object will be the new type: `NativeCommandException` with the following details: + +| Property | Definition +---------------- | ------------------- +| ExitCode: | The exit code of the failed command. +| ErrorID: | `"Program {0} ended with non-zero exit code {1}"`, with the command name and the exit code, from resource string `ProgramFailedToComplete`. +| ErrorCategory: | `ErrorCategory.NotSpecified`. +| object: | exit code +| Source: | The full path to the application +| ProcessInfo | details of failed command including path, exit code, and PID + +## Alternative Approaches and Considerations + +### Native commands should respect $ErrorActionPreference + +Native commands should respect $ErrorActionPreference and not need $PSNativeCommandErrorAction. This +could be released as an experimental feature. + +### Extending $PSNativeCommandErrorAction + +For users that prefer to set a single preference variable that affects both PowerShell and native +commands, The values of `$PSNativeCommandErrorAction` may be extended to include +`MatchErrorActionPreference`, which should apply the `$ErrorActionPreference` setting to native +commands. + +- A conversion will occur between `$PSNativeCommandErrorAction` and `$ErrorActionPreference` values, + where `MatchErrorActionPreference` is converted to the current value of `$ErrorActionPreference` + +### Explicit invocation logic + +One way of checking for a single native command and handling its exit +status explicitly would be to put this logic into a script block and call it with the invocation +operator (`&`). + +```Powershell +if ($LASTEXITCODE -ne 0) +{ + throw "Command failed. See above errors for details" +} + ``` + +#### Convert non-terminating errors to terminating where the command output is used + +This implements semantics equivalent to bash `set -eo pipefail` in the runtime layer. + +The `$PSStrictPipeLine` preference variable would govern promotion of a non-terminating error to a +terminating error on getting an object from the pipeline output stream. Possible values would be: + +- `$false`: (the default) an object can be collected from the pipeline output stream regardless of + the command exit value. This is the same as existing PowerShell treatment of this case. +- `$true`: where the exit status of a native command is `$false`, trying to get an object from its + output stream will create a terminating error from the non-terminating errors in its error stream. + Conversion to boolean would be structured to ensure that this returns `$false` if the output + pipeline does not contain anything without trying to get an actual value from it. + +This would allow syntax like `if`, `while` and pipeline chain operators to be usefully combined with +native commands. + +This approach is less desirable because it's based on the command output instead of the return code. + +### Set-StrictMode + +A common configuration command for POSIX scripts `set -euo pipefail` includes the `set -u` +configuration which returns an error if any variable has not been previously defined. This is +equivalent to the existing PowerShell `Set-StrictMode` and is not needed to be addressed in this RFC. + +### Add "strict" native command option + +The `$PSStrictNativeCommand` preference should treat creation of an `ErrorRecord` for native +commands in the same way as this is treated elsewhere. Described here as a Boolean, could be +considered as an enum to allow for future expansion. Possible values are: + +- `$false`: (the default) ignore non-zero exit codes. This is the same as existing PowerShell + treatment of this case. +- `$true`: Populate the error stream of the native command with an `ErrorRecord` associated with an + `ExitException` exception. \ No newline at end of file From 5d04d210764c018beaf4c444a1b81ad55c80bddf Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Mon, 22 Mar 2021 14:22:57 -0700 Subject: [PATCH 02/16] updated demo.txt and spec --- .../RFC00XX-Native-Command-Error-Handling.md | 227 ++++++++++++++---- 1 file changed, 177 insertions(+), 50 deletions(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index 2e8bd8b9c..e197f03f5 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -46,13 +46,13 @@ support all cases as `$?` can be false from a cmdlet or function error, making ` stale. In POSIX shells, this need to terminate on command error is addressed by the `set -e` configuration, -which causes the shell to exit when a command fails. In addition, to ensure that an error is +which causes the script to exit when a command fails. In addition, to ensure that an error is returned if any command in a pipeline fails, POSIX shells address this need with `set -o pipefail` configuration. This specification proposes a similar idea, but adapted to the PowerShell conventions of preference variables and catchable, self-describing, terminating error objects. This proposal adds the -equivalent functionality of `set -eo pipefail` to return an error if any command in a pipeline +equivalent functionality of `set -eo pipefail` to stop execution and return an error if any command in a pipeline fails. The specification and alternative proposals are based on the @@ -70,40 +70,197 @@ boilerplate. The specification proposes similar functionality to the common POSIX shell configuration `set -eo pipefail`. +- `set -u` - returns an error if any variable has not been previously defined. - `set -e` - instructs to immediately exit if any command has a non-zero exit status. - `set -o pipefail` - prevents errors in the pipeline from being masked. The return code for the non-zero error is returned for the entire pipeline. +### set -u/ Set-StrictMode + +In the example below, `set -u` is equivalent to `Set-StrictMode -Version 2.0`. + +```bash +#!/bin/bash + +/bin/echo "Without set -u : No output - No error is produced" +/bin/echo "$firstname" + +/bin/echo "" + +/bin/echo "With set -u : Equivalent to Set-StrictMode -Version 2.0" +set -u +/bin/echo "$firstname" +``` + +```output +Without set -u : No output - No error is produced + + +With set -u : Equivalent to Set-StrictMode -Version 2.0 +/Users/jasonhelmick/natcmdbash/strict: line 10: firstname: unbound variable +``` + +```powershell +# In PowerShell + +/bin/echo "Without Set-StrictMode -version 2.0 : No output - No error is produced" +/bin/echo "$firstname" + +/bin/echo "" + +/bin/echo "With Set-StrictMode -version 2.0 : Equivalent to set -u" +Set-StrictMode -version 2.0 +/bin/echo "$firstname" +``` + +```output +Without set -u : No output - No error is produced + + +With Set-StrictMode -version 2.0 : Equivalent to set -u +InvalidOperation: /Users/jasonhelmick/natcmdbash/psstrict.ps1:9 +Line | + 9 | /bin/echo "$firstname" + | ~~~~~~~~~~ + | The variable '$firstname' cannot be retrieved because it has not been set. +``` + +### set -e/ $ErrorActionPreference + +In the example below, `set -e` is not equivalent to `$ErrorActionPReference` for native commands. + +```bash +#!/bin/bash + +/bin/echo "Without set -e : Will receive message after failure" +/bin/cat ./nofile +/bin/echo "Message After failure" + +/bin/echo "" + +/bin/echo "With set -e : Will NOT receive message after failure" +set -e +/bin/cat ./nofile +/bin/echo "Message After failure" +``` + +```output +Without set -e : Will receive message after failure +cat: ./nofile: No such file or directory +Message After failure + +With set -e : Will NOT receive message after failure +cat: ./nofile: No such file or directory +``` + ```powershell +# In Powershell -Cat ./Nofile | head -n 1 -Cat: ./Nofile: No such file or directory -echo $? - +/bin/echo "Without $ErrorActionPreference : Will receive message after failure" +/bin/cat ./nofile +/bin/echo "Message After failure" -$ErrorActionPreference = 'Stop' # This is equivalent to Set -e +/bin/echo "" -Cat ./Nofile | head -n 1 -Cat: ./Nofile: No such file or directory -echo $? - +/bin/echo "With `$ErrorActionPreference = 'Stop' : SHOULD NOT receive message after failure - but does" +$ErrorActionPreference = "Stop" +/bin/cat ./nofile +/bin/echo "Message After failure" -$PSNativeCommandErrorAction ='Stop' # Equivalent to Set -o pipefail -cat ./nofile | head -n 1 -Cat: ./Nofile: No such file or directory -echo $? - +/bin/echo "" +Write-Host "With cmdlet's - `$ErrorActionPreference = 'Stop' : Will NOT receive message after failure" +$ErrorActionPreference = 'Stop' +Get-Content ./nofile +Write-Host "Message After failure" +``` + +```output +Without $ErrorActionPreference : Will receive message after failure +cat: ./nofile: No such file or directory +Message After failure + +With $ErrorActionPreference = 'Stop' : SHOULD NOT receive message after failure - but does +cat: ./nofile: No such file or directory +Message After failure + +With cmdlet's - $ErrorActionPreference = 'Stop' : Will NOT receive message after failure +Get-Content: /Users/jasonhelmick/natcmdbash/psstop.ps1:17 +Line | + 17 | Get-Content ./nofile + | ~~~~~~~~~~~~~~~~~~~~ + | Cannot find path '/Users/jasonhelmick/natcmdbash/nofile' because it does not exist. +``` + +### set -o pipefail/ PSNativeCommandErrorAction + +In the example below, PowerShell has no equivalent to `set -o pipefail` for native commands. + +```bash +#!/bin/bash + +/bin/echo "Without set -o pipefail : returns 0" +/bin/cat ./nofile | /bin/echo "pipe statement after failure" +/bin/echo "returns $?" + +/bin/echo "" + +/bin/echo "With set -o pipefail : returns non-zero" +set -o pipefail +/bin/cat ./nofile | /bin/echo "pipe statement after failure" +/bin/echo "returns $?" +``` + +```output +Without set -o pipefail : returns 0 +pipe statement after failure +cat: ./nofile: No such file or directory +returns 0 + +With set -o pipefail : returns non-zero +cat: ./nofile: No such file or directory +pipe statement after failure +returns 1 +``` + +```powershell +# In PowerShell + +/bin/echo "Without `$PSNativeCommandErrorAction = 'Stop' : should return 0 (true)" +/bin/cat ./nofile | /bin/echo "pipe statement after failure" +/bin/echo "returns $?" + +/bin/echo "" + +/bin/echo "With set -o equiv. `$PSNativeCommandErrorAction = 'Stop' : should return non-zero (false)" +$PSNativeCommandErrorAction = 'Stop' +/bin/cat ./nofile | /bin/echo "pipe statement after failure" +/bin/echo "returns $?" +``` + +```output +Without `$PSNativeCommandErrorAction = 'Stop' : should return 0 (true) +cat: ./nofile: No such file or directory +pipe statement after failure +returns False + +With set -o equiv. = 'Stop' : should return non-zero (false) +cat: ./nofile: No such file or directory +pipe statement after failure +returns False ``` > [!NOTE] A common configuration command for POSIX shells `set -euo pipefail` includes the `set -u` > configuration which returns an error if any variable has not been previously defined. This is -> equivalent to the existing PowerShell `Set-StrictMode` and is not addressed in this RFC. +> equivalent to the existing PowerShell `Set-StrictMode` and does not need to be addressed in this +> RFC. + +### $PSNativeCommandErrorAction The `$PSNativeCommandErrorAction` preference variable will implement a version of the `$ErrorActionPreference` variable for native commands. -- The value will default to `Ignore` for compatibility with existing behavior. +- The value will default to `Continue` for compatibility with existing behavior. - For non-zero exit codes and except for the value `Ignore`, an `ErrorRecord` will be added to `$Error` that wraps the exit code and the command executed that returned the exit code. - Initially, only the existing values of `$ErrorActionPreference` will be supported in @@ -114,7 +271,7 @@ The `$PSNativeCommandErrorAction` preference variable will implement a version o | Value | Definition ---------------- | ------------------- | Break | Enter the debugger when an error occurs or when an exception is raised. -| Continue | (Default) - Displays the error message and continues executing. +| Continue | Displays the error message and continues executing. | Ignore | Suppresses the error message and continues to execute the command. | Inquire | Displays the error message and asks you whether you want to continue. | SilentlyContinue| No effect. The error message isn't displayed and execution continues without interruption. @@ -124,7 +281,7 @@ The `$PSNativeCommandErrorAction` preference variable will implement a version o ### Preference variable resolves error handling conflicts In cases where an existing script already handles non-zero native command errors, the preference -variable `$PSNativeCommandErrorAction` may be set to `Ignore`. Error handling behavior of the +variable `$PSNativeCommandErrorAction` may be set to `Continue`. Error handling behavior of the PowerShell cmdlets is handled separately with `$ErrorActionPreference`. ### Error object @@ -170,38 +327,8 @@ if ($LASTEXITCODE -ne 0) } ``` -#### Convert non-terminating errors to terminating where the command output is used - -This implements semantics equivalent to bash `set -eo pipefail` in the runtime layer. - -The `$PSStrictPipeLine` preference variable would govern promotion of a non-terminating error to a -terminating error on getting an object from the pipeline output stream. Possible values would be: - -- `$false`: (the default) an object can be collected from the pipeline output stream regardless of - the command exit value. This is the same as existing PowerShell treatment of this case. -- `$true`: where the exit status of a native command is `$false`, trying to get an object from its - output stream will create a terminating error from the non-terminating errors in its error stream. - Conversion to boolean would be structured to ensure that this returns `$false` if the output - pipeline does not contain anything without trying to get an actual value from it. - -This would allow syntax like `if`, `while` and pipeline chain operators to be usefully combined with -native commands. - -This approach is less desirable because it's based on the command output instead of the return code. - ### Set-StrictMode A common configuration command for POSIX scripts `set -euo pipefail` includes the `set -u` configuration which returns an error if any variable has not been previously defined. This is equivalent to the existing PowerShell `Set-StrictMode` and is not needed to be addressed in this RFC. - -### Add "strict" native command option - -The `$PSStrictNativeCommand` preference should treat creation of an `ErrorRecord` for native -commands in the same way as this is treated elsewhere. Described here as a Boolean, could be -considered as an enum to allow for future expansion. Possible values are: - -- `$false`: (the default) ignore non-zero exit codes. This is the same as existing PowerShell - treatment of this case. -- `$true`: Populate the error stream of the native command with an `ErrorRecord` associated with an - `ExitException` exception. \ No newline at end of file From a98580436f04cacd3dbf697197afed2f9aed229a Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Tue, 23 Mar 2021 08:06:05 -0700 Subject: [PATCH 03/16] Update 1-Draft/RFC00XX-Native-Command-Error-Handling.md Co-authored-by: James Truher [MSFT] --- 1-Draft/RFC00XX-Native-Command-Error-Handling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index e197f03f5..7b7881c0b 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -52,7 +52,7 @@ configuration. This specification proposes a similar idea, but adapted to the PowerShell conventions of preference variables and catchable, self-describing, terminating error objects. This proposal adds the -equivalent functionality of `set -eo pipefail` to stop execution and return an error if any command in a pipeline +equivalent functionality of `set -e -o pipefail` (abbreviated to `set -eo pipefail`) to stop execution and return an error if any command in a pipeline fails. The specification and alternative proposals are based on the From c03f3df392ccc9ddaffe02025359f6290b988dcf Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Tue, 23 Mar 2021 08:06:28 -0700 Subject: [PATCH 04/16] Update 1-Draft/RFC00XX-Native-Command-Error-Handling.md Co-authored-by: James Truher [MSFT] --- 1-Draft/RFC00XX-Native-Command-Error-Handling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index 7b7881c0b..992a166ab 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -45,7 +45,7 @@ Simply relaying the errors through the error stream isn't the solution. The exam support all cases as `$?` can be false from a cmdlet or function error, making `$LASTEXITCODE` stale. -In POSIX shells, this need to terminate on command error is addressed by the `set -e` configuration, +In POSIX shells, terminating execution when a command has an error is enabled via executing `set -e` in the session. which causes the script to exit when a command fails. In addition, to ensure that an error is returned if any command in a pipeline fails, POSIX shells address this need with `set -o pipefail` configuration. From 1a38126c8adc06dee1c488148fe45632eba3c0c6 Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Tue, 23 Mar 2021 08:06:48 -0700 Subject: [PATCH 05/16] Update 1-Draft/RFC00XX-Native-Command-Error-Handling.md Co-authored-by: James Truher [MSFT] --- 1-Draft/RFC00XX-Native-Command-Error-Handling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index 992a166ab..72ffcd0ba 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -46,7 +46,7 @@ support all cases as `$?` can be false from a cmdlet or function error, making ` stale. In POSIX shells, terminating execution when a command has an error is enabled via executing `set -e` in the session. -which causes the script to exit when a command fails. In addition, to ensure that an error is +In addition, to ensure that an error is returned if any command in a pipeline fails, POSIX shells address this need with `set -o pipefail` configuration. From d29e4c33577d411f88c4566e322fac2eb0b2452c Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Tue, 23 Mar 2021 08:07:03 -0700 Subject: [PATCH 06/16] Update 1-Draft/RFC00XX-Native-Command-Error-Handling.md Co-authored-by: James Truher [MSFT] --- 1-Draft/RFC00XX-Native-Command-Error-Handling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index 72ffcd0ba..fc4acc934 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -47,7 +47,7 @@ stale. In POSIX shells, terminating execution when a command has an error is enabled via executing `set -e` in the session. In addition, to ensure that an error is -returned if any command in a pipeline fails, POSIX shells address this need with `set -o pipefail` +returned if any command in a pipeline fails, POSIX shells address this via executing `set -o pipefail` configuration. This specification proposes a similar idea, but adapted to the PowerShell conventions of preference From 77f46e232400aa2459984bcf81357e099fbb0d95 Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Tue, 23 Mar 2021 08:07:22 -0700 Subject: [PATCH 07/16] Update 1-Draft/RFC00XX-Native-Command-Error-Handling.md Co-authored-by: James Truher [MSFT] --- 1-Draft/RFC00XX-Native-Command-Error-Handling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index fc4acc934..eb53b4a72 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -48,7 +48,7 @@ stale. In POSIX shells, terminating execution when a command has an error is enabled via executing `set -e` in the session. In addition, to ensure that an error is returned if any command in a pipeline fails, POSIX shells address this via executing `set -o pipefail` -configuration. +in the session. This specification proposes a similar idea, but adapted to the PowerShell conventions of preference variables and catchable, self-describing, terminating error objects. This proposal adds the From bef6778d6137edd5b81c681377d9f284e0dd3b50 Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Tue, 23 Mar 2021 08:07:38 -0700 Subject: [PATCH 08/16] Update 1-Draft/RFC00XX-Native-Command-Error-Handling.md Co-authored-by: James Truher [MSFT] --- 1-Draft/RFC00XX-Native-Command-Error-Handling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index eb53b4a72..6d4806fdd 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -274,7 +274,7 @@ The `$PSNativeCommandErrorAction` preference variable will implement a version o | Continue | Displays the error message and continues executing. | Ignore | Suppresses the error message and continues to execute the command. | Inquire | Displays the error message and asks you whether you want to continue. -| SilentlyContinue| No effect. The error message isn't displayed and execution continues without interruption. +| SilentlyContinue| The error message isn't displayed and execution continues without interruption. | Stop | Displays the error message and stops executing. In addition to the error generated, the Stop value generates an ActionPreferenceStopException object to the error stream. stream | Suspend | Automatically suspends a workflow job to allow for further investigation. From fce539d594d7e29611cb6113ede18bc0a847ed6b Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Tue, 23 Mar 2021 08:07:57 -0700 Subject: [PATCH 09/16] Update 1-Draft/RFC00XX-Native-Command-Error-Handling.md Co-authored-by: James Truher [MSFT] --- 1-Draft/RFC00XX-Native-Command-Error-Handling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index 6d4806fdd..622e77d85 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -301,7 +301,7 @@ The reported error record object will be the new type: `NativeCommandException` ### Native commands should respect $ErrorActionPreference -Native commands should respect $ErrorActionPreference and not need $PSNativeCommandErrorAction. This +Native commands should use the $ErrorActionPreference setting and not need $PSNativeCommandErrorAction. This could be released as an experimental feature. ### Extending $PSNativeCommandErrorAction From e274268e40d42394dc5ba62034a03097809fffd5 Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Tue, 23 Mar 2021 08:20:49 -0700 Subject: [PATCH 10/16] updated post Jim review --- .../RFC00XX-Native-Command-Error-Handling.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index 622e77d85..619793cc5 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -45,15 +45,14 @@ Simply relaying the errors through the error stream isn't the solution. The exam support all cases as `$?` can be false from a cmdlet or function error, making `$LASTEXITCODE` stale. -In POSIX shells, terminating execution when a command has an error is enabled via executing `set -e` in the session. -In addition, to ensure that an error is -returned if any command in a pipeline fails, POSIX shells address this via executing `set -o pipefail` -in the session. +In POSIX shells, terminating execution when a command has an error is enabled via executing `set -e` +in the session. In addition, to ensure that an error is returned if any command in a pipeline fails, +POSIX shells address this via executing `set -o pipefail` in the session. This specification proposes a similar idea, but adapted to the PowerShell conventions of preference variables and catchable, self-describing, terminating error objects. This proposal adds the -equivalent functionality of `set -e -o pipefail` (abbreviated to `set -eo pipefail`) to stop execution and return an error if any command in a pipeline -fails. +equivalent functionality of `set -e -o pipefail` (abbreviated to `set -eo pipefail`) to stop +execution and return an error if any command in a pipeline fails. The specification and alternative proposals are based on the [Equivalent of bash `set -e` #3415](https://github.com/PowerShell/PowerShell/issues/3415) @@ -276,7 +275,7 @@ The `$PSNativeCommandErrorAction` preference variable will implement a version o | Inquire | Displays the error message and asks you whether you want to continue. | SilentlyContinue| The error message isn't displayed and execution continues without interruption. | Stop | Displays the error message and stops executing. In addition to the error generated, the Stop value generates an ActionPreferenceStopException object to the error stream. stream -| Suspend | Automatically suspends a workflow job to allow for further investigation. +| Suspend | Automatically suspends a job to allow for further investigation. ### Preference variable resolves error handling conflicts @@ -293,16 +292,17 @@ The reported error record object will be the new type: `NativeCommandException` | ExitCode: | The exit code of the failed command. | ErrorID: | `"Program {0} ended with non-zero exit code {1}"`, with the command name and the exit code, from resource string `ProgramFailedToComplete`. | ErrorCategory: | `ErrorCategory.NotSpecified`. -| object: | exit code +| object: | Exit code | Source: | The full path to the application -| ProcessInfo | details of failed command including path, exit code, and PID +| ProcessInfo | Details of failed command including path, exit code, and PID +| TargetObject | Specifies the object that was being processed when the error occurred. ## Alternative Approaches and Considerations ### Native commands should respect $ErrorActionPreference -Native commands should use the $ErrorActionPreference setting and not need $PSNativeCommandErrorAction. This -could be released as an experimental feature. +Native commands should use the $ErrorActionPreference setting and not need +$PSNativeCommandErrorAction. This could be released as an experimental feature. ### Extending $PSNativeCommandErrorAction @@ -311,8 +311,8 @@ commands, The values of `$PSNativeCommandErrorAction` may be extended to include `MatchErrorActionPreference`, which should apply the `$ErrorActionPreference` setting to native commands. -- A conversion will occur between `$PSNativeCommandErrorAction` and `$ErrorActionPreference` values, - where `MatchErrorActionPreference` is converted to the current value of `$ErrorActionPreference` +- If `$PSNativeCommandErrorAction` is set to `MatchErrorActionPreference` then the value of + `$ErrorActionPreference` will define the behavior for native command errors. ### Explicit invocation logic From 177a002eb25325381a909088da0112669dea8709 Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Tue, 23 Mar 2021 08:23:13 -0700 Subject: [PATCH 11/16] Apply suggestions from code review Co-authored-by: James Truher [MSFT] --- 1-Draft/RFC00XX-Native-Command-Error-Handling.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index 619793cc5..94164b3bc 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -62,8 +62,8 @@ execution and return an error if any command in a pipeline fails. ## Specification -This RFC proposes a preference variable to configure the elevation of errors produced by native -commands to first-class PowerShell errors, so that native command failures will produce error +This RFC proposes a preference variable to enable errors produced by native +commands to be PowerShell errors, so that failures will produce error objects that are added to the error stream and may terminate execution of the script without added boilerplate. @@ -76,7 +76,7 @@ The specification proposes similar functionality to the common POSIX shell confi ### set -u/ Set-StrictMode -In the example below, `set -u` is equivalent to `Set-StrictMode -Version 2.0`. +In the example below, `set -u` behavior of `bash` is shown followed by the proposed behavior for PowerShell. ```bash #!/bin/bash From 98a10557b0f9f8b9cbc3dfd2bd63e9c9ff344a0b Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Tue, 23 Mar 2021 08:32:18 -0700 Subject: [PATCH 12/16] cleaned demo.txt --- .../RFC00XX-Native-Command-Error-Handling.md | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index 94164b3bc..c21d403dc 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -62,12 +62,12 @@ execution and return an error if any command in a pipeline fails. ## Specification -This RFC proposes a preference variable to enable errors produced by native -commands to be PowerShell errors, so that failures will produce error -objects that are added to the error stream and may terminate execution of the script without added -boilerplate. +This RFC proposes a preference variable to enable errors produced by native commands to be +PowerShell errors, so that failures will produce error objects that are added to the error stream +and may terminate execution of the script without added boilerplate. -The specification proposes similar functionality to the common POSIX shell configuration `set -eo pipefail`. +The specification proposes similar functionality to the common POSIX shell configuration +`set -eo pipefail`. - `set -u` - returns an error if any variable has not been previously defined. - `set -e` - instructs to immediately exit if any command has a non-zero exit status. @@ -76,16 +76,14 @@ The specification proposes similar functionality to the common POSIX shell confi ### set -u/ Set-StrictMode -In the example below, `set -u` behavior of `bash` is shown followed by the proposed behavior for PowerShell. +In the example below, `set -u` behavior of `bash` is shown followed by the proposed behavior for +PowerShell. This is not part of this proposal, but added for clarity. ```bash #!/bin/bash - /bin/echo "Without set -u : No output - No error is produced" /bin/echo "$firstname" - /bin/echo "" - /bin/echo "With set -u : Equivalent to Set-StrictMode -Version 2.0" set -u /bin/echo "$firstname" @@ -94,19 +92,15 @@ set -u ```output Without set -u : No output - No error is produced - With set -u : Equivalent to Set-StrictMode -Version 2.0 /Users/jasonhelmick/natcmdbash/strict: line 10: firstname: unbound variable ``` ```powershell # In PowerShell - /bin/echo "Without Set-StrictMode -version 2.0 : No output - No error is produced" /bin/echo "$firstname" - /bin/echo "" - /bin/echo "With Set-StrictMode -version 2.0 : Equivalent to set -u" Set-StrictMode -version 2.0 /bin/echo "$firstname" @@ -115,7 +109,6 @@ Set-StrictMode -version 2.0 ```output Without set -u : No output - No error is produced - With Set-StrictMode -version 2.0 : Equivalent to set -u InvalidOperation: /Users/jasonhelmick/natcmdbash/psstrict.ps1:9 Line | @@ -130,13 +123,10 @@ In the example below, `set -e` is not equivalent to `$ErrorActionPReference` for ```bash #!/bin/bash - /bin/echo "Without set -e : Will receive message after failure" /bin/cat ./nofile /bin/echo "Message After failure" - /bin/echo "" - /bin/echo "With set -e : Will NOT receive message after failure" set -e /bin/cat ./nofile @@ -154,20 +144,15 @@ cat: ./nofile: No such file or directory ```powershell # In Powershell - /bin/echo "Without $ErrorActionPreference : Will receive message after failure" /bin/cat ./nofile /bin/echo "Message After failure" - /bin/echo "" - /bin/echo "With `$ErrorActionPreference = 'Stop' : SHOULD NOT receive message after failure - but does" $ErrorActionPreference = "Stop" /bin/cat ./nofile /bin/echo "Message After failure" - /bin/echo "" - Write-Host "With cmdlet's - `$ErrorActionPreference = 'Stop' : Will NOT receive message after failure" $ErrorActionPreference = 'Stop' Get-Content ./nofile @@ -197,13 +182,10 @@ In the example below, PowerShell has no equivalent to `set -o pipefail` for nati ```bash #!/bin/bash - /bin/echo "Without set -o pipefail : returns 0" /bin/cat ./nofile | /bin/echo "pipe statement after failure" /bin/echo "returns $?" - /bin/echo "" - /bin/echo "With set -o pipefail : returns non-zero" set -o pipefail /bin/cat ./nofile | /bin/echo "pipe statement after failure" @@ -224,13 +206,10 @@ returns 1 ```powershell # In PowerShell - /bin/echo "Without `$PSNativeCommandErrorAction = 'Stop' : should return 0 (true)" /bin/cat ./nofile | /bin/echo "pipe statement after failure" /bin/echo "returns $?" - /bin/echo "" - /bin/echo "With set -o equiv. `$PSNativeCommandErrorAction = 'Stop' : should return non-zero (false)" $PSNativeCommandErrorAction = 'Stop' /bin/cat ./nofile | /bin/echo "pipe statement after failure" From ac7edb825e3e65712677ca5af5159a61c549e00a Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Tue, 23 Mar 2021 08:45:39 -0700 Subject: [PATCH 13/16] added bash/powershell demo clarity --- 1-Draft/RFC00XX-Native-Command-Error-Handling.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index c21d403dc..bf4f14566 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -80,6 +80,7 @@ In the example below, `set -u` behavior of `bash` is shown followed by the propo PowerShell. This is not part of this proposal, but added for clarity. ```bash +bash-3.2$ cat file #!/bin/bash /bin/echo "Without set -u : No output - No error is produced" /bin/echo "$firstname" @@ -90,6 +91,7 @@ set -u ``` ```output +bash-3.2$ file Without set -u : No output - No error is produced With set -u : Equivalent to Set-StrictMode -Version 2.0 @@ -97,7 +99,7 @@ With set -u : Equivalent to Set-StrictMode -Version 2.0 ``` ```powershell -# In PowerShell +PS> cat ./file.ps1 /bin/echo "Without Set-StrictMode -version 2.0 : No output - No error is produced" /bin/echo "$firstname" /bin/echo "" @@ -107,6 +109,7 @@ Set-StrictMode -version 2.0 ``` ```output +PS> ./file.ps1 Without set -u : No output - No error is produced With Set-StrictMode -version 2.0 : Equivalent to set -u @@ -122,6 +125,7 @@ Line | In the example below, `set -e` is not equivalent to `$ErrorActionPReference` for native commands. ```bash +bash-3.2$ cat file #!/bin/bash /bin/echo "Without set -e : Will receive message after failure" /bin/cat ./nofile @@ -134,6 +138,7 @@ set -e ``` ```output +bash-3.2$ file Without set -e : Will receive message after failure cat: ./nofile: No such file or directory Message After failure @@ -143,7 +148,7 @@ cat: ./nofile: No such file or directory ``` ```powershell -# In Powershell +PS> cat ./file.ps1 /bin/echo "Without $ErrorActionPreference : Will receive message after failure" /bin/cat ./nofile /bin/echo "Message After failure" @@ -160,6 +165,7 @@ Write-Host "Message After failure" ``` ```output +PS> ./file.ps1 Without $ErrorActionPreference : Will receive message after failure cat: ./nofile: No such file or directory Message After failure @@ -181,6 +187,7 @@ Line | In the example below, PowerShell has no equivalent to `set -o pipefail` for native commands. ```bash +bash-3.2$ cat file #!/bin/bash /bin/echo "Without set -o pipefail : returns 0" /bin/cat ./nofile | /bin/echo "pipe statement after failure" @@ -193,6 +200,7 @@ set -o pipefail ``` ```output +bash-3.2$ file Without set -o pipefail : returns 0 pipe statement after failure cat: ./nofile: No such file or directory @@ -205,7 +213,7 @@ returns 1 ``` ```powershell -# In PowerShell +PS> cat ./file.ps1 /bin/echo "Without `$PSNativeCommandErrorAction = 'Stop' : should return 0 (true)" /bin/cat ./nofile | /bin/echo "pipe statement after failure" /bin/echo "returns $?" @@ -217,6 +225,7 @@ $PSNativeCommandErrorAction = 'Stop' ``` ```output +PS> ./file.ps1 Without `$PSNativeCommandErrorAction = 'Stop' : should return 0 (true) cat: ./nofile: No such file or directory pipe statement after failure From 15f408d183ad504b88a2d7fa326d8569936d9bbe Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Sat, 27 Mar 2021 10:40:56 -0700 Subject: [PATCH 14/16] improved demo.txt --- .../RFC00XX-Native-Command-Error-Handling.md | 187 ++++++++++-------- 1 file changed, 100 insertions(+), 87 deletions(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index bf4f14566..d859f8ff5 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -79,106 +79,113 @@ The specification proposes similar functionality to the common POSIX shell confi In the example below, `set -u` behavior of `bash` is shown followed by the proposed behavior for PowerShell. This is not part of this proposal, but added for clarity. +cat ./testfile.sh + ```bash -bash-3.2$ cat file + #!/bin/bash -/bin/echo "Without set -u : No output - No error is produced" -/bin/echo "$firstname" -/bin/echo "" -/bin/echo "With set -u : Equivalent to Set-StrictMode -Version 2.0" +echo "Without set -u : No output - No error is produced" +echo "$firstname" +echo "With set -u : Equivalent to Set-StrictMode -Version 2.0" set -u -/bin/echo "$firstname" +echo "$firstname" ``` +./testfile.sh + ```output -bash-3.2$ file Without set -u : No output - No error is produced With set -u : Equivalent to Set-StrictMode -Version 2.0 -/Users/jasonhelmick/natcmdbash/strict: line 10: firstname: unbound variable +./testfile.sh: line 7: firstname: unbound variable ``` +Get-Content ./testfile.ps1 + ```powershell -PS> cat ./file.ps1 -/bin/echo "Without Set-StrictMode -version 2.0 : No output - No error is produced" -/bin/echo "$firstname" -/bin/echo "" -/bin/echo "With Set-StrictMode -version 2.0 : Equivalent to set -u" + +Write-Output "Without Set-StrictMode -version 2.0 : No output - No error is produced" +Write-Output "$firstname" +Write-Output "With Set-StrictMode -version 2.0 : Equivalent to set -u" Set-StrictMode -version 2.0 -/bin/echo "$firstname" +Write-Output "$firstname" ``` +./testfile.ps1 + ```output -PS> ./file.ps1 -Without set -u : No output - No error is produced +Without Set-StrictMode -version 2.0 : No output - No error is produced With Set-StrictMode -version 2.0 : Equivalent to set -u -InvalidOperation: /Users/jasonhelmick/natcmdbash/psstrict.ps1:9 +InvalidOperation: /Users/jasonhelmick/natcmdbash/testfile.ps1:5 Line | - 9 | /bin/echo "$firstname" - | ~~~~~~~~~~ + 5 | Write-Output "$firstname" + | ~~~~~~~~~~ | The variable '$firstname' cannot be retrieved because it has not been set. ``` ### set -e/ $ErrorActionPreference -In the example below, `set -e` is not equivalent to `$ErrorActionPReference` for native commands. +In the example below, `set -e` is not equivalent to `$ErrorActionPreference` for native commands. + +cat ./testfile.sh ```bash -bash-3.2$ cat file #!/bin/bash -/bin/echo "Without set -e : Will receive message after failure" -/bin/cat ./nofile -/bin/echo "Message After failure" -/bin/echo "" -/bin/echo "With set -e : Will NOT receive message after failure" +echo "Without set -e : Will receive message after failure" +cat ./nofile +echo "Message After failure" +echo "With set -e : Will NOT continue script execution after failure" set -e -/bin/cat ./nofile -/bin/echo "Message After failure" +cat ./nofile +echo "Message After failure" ``` +./testfile.sh + ```output -bash-3.2$ file Without set -e : Will receive message after failure cat: ./nofile: No such file or directory Message After failure - -With set -e : Will NOT receive message after failure +With set -e : Will NOT continue script execution after failure cat: ./nofile: No such file or directory ``` +Get-Content ./testfile.ps1 + ```powershell -PS> cat ./file.ps1 -/bin/echo "Without $ErrorActionPreference : Will receive message after failure" -/bin/cat ./nofile -/bin/echo "Message After failure" -/bin/echo "" -/bin/echo "With `$ErrorActionPreference = 'Stop' : SHOULD NOT receive message after failure - but does" +Write-Output "Without `$ErrorActionPreference : Will receive message after failure" +Get-Content -path ./nofile +Write-Output "Message After failure" +/bin/echo "With (Bash) `$ErrorActionPreference = 'Stop' : SHOULD NOT continue script execution after failure - but does" $ErrorActionPreference = "Stop" /bin/cat ./nofile /bin/echo "Message After failure" -/bin/echo "" -Write-Host "With cmdlet's - `$ErrorActionPreference = 'Stop' : Will NOT receive message after failure" -$ErrorActionPreference = 'Stop' -Get-Content ./nofile -Write-Host "Message After failure" +Write-Output "With (PowerShell) `$ErrorActionPreference = 'Stop' : SHOULD NOT continue script execution after failure" +$ErrorActionPreference = "Stop" +Get-Content -path ./nofile +Write-Output "Message After failure" ``` +./testfile.ps1 + ```output -PS> ./file.ps1 Without $ErrorActionPreference : Will receive message after failure -cat: ./nofile: No such file or directory -Message After failure +Get-Content: /Users/jasonhelmick/natcmdbash/testfile.ps1:2 +Line | + 2 | Get-Content -path ./nofile + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + | Cannot find path '/Users/jasonhelmick/natcmdbash/nofile' because it does not exist. -With $ErrorActionPreference = 'Stop' : SHOULD NOT receive message after failure - but does +Message After failure +With (Bash) $ErrorActionPreference = 'Stop' : SHOULD NOT continue script execution after failure - but does cat: ./nofile: No such file or directory Message After failure - -With cmdlet's - $ErrorActionPreference = 'Stop' : Will NOT receive message after failure -Get-Content: /Users/jasonhelmick/natcmdbash/psstop.ps1:17 +With (PowerShell) $ErrorActionPreference = 'Stop' : SHOULD NOT continue script execution after failure +Get-Content: /Users/jasonhelmick/natcmdbash/testfile.ps1:10 Line | - 17 | Get-Content ./nofile - | ~~~~~~~~~~~~~~~~~~~~ + 10 | Get-Content -path ./nofile + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ | Cannot find path '/Users/jasonhelmick/natcmdbash/nofile' because it does not exist. ``` @@ -186,55 +193,61 @@ Line | In the example below, PowerShell has no equivalent to `set -o pipefail` for native commands. +cat ./testfile.sh + ```bash -bash-3.2$ cat file #!/bin/bash -/bin/echo "Without set -o pipefail : returns 0" -/bin/cat ./nofile | /bin/echo "pipe statement after failure" -/bin/echo "returns $?" -/bin/echo "" -/bin/echo "With set -o pipefail : returns non-zero" +echo "Without set -o pipefail : returns zero (true)" +cat ./nofile | echo "pipe statement after failure" +echo "returns $?" +echo "With set -o pipefail : returns non-zero (false)" set -o pipefail -/bin/cat ./nofile | /bin/echo "pipe statement after failure" -/bin/echo "returns $?" +cat ./nofile | echo "pipe statement after failure" +echo "returns $?" ``` +./testfile.sh + ```output -bash-3.2$ file -Without set -o pipefail : returns 0 +Without set -o pipefail : returns zero (true) pipe statement after failure cat: ./nofile: No such file or directory returns 0 - -With set -o pipefail : returns non-zero -cat: ./nofile: No such file or directory +With set -o pipefail : returns non-zero (false) pipe statement after failure +cat: ./nofile: No such file or directory returns 1 ``` +Get-Content ./testfile.ps1 + ```powershell -PS> cat ./file.ps1 -/bin/echo "Without `$PSNativeCommandErrorAction = 'Stop' : should return 0 (true)" -/bin/cat ./nofile | /bin/echo "pipe statement after failure" -/bin/echo "returns $?" -/bin/echo "" -/bin/echo "With set -o equiv. `$PSNativeCommandErrorAction = 'Stop' : should return non-zero (false)" -$PSNativeCommandErrorAction = 'Stop' -/bin/cat ./nofile | /bin/echo "pipe statement after failure" -/bin/echo "returns $?" +Write-Output "Without `$ErrorActionPreference = 'Stop' : should return zero (true)" +Get-Content -path ./nofile | Write-Output "pipe statement after failure" +Write-Output "returns $?" +Write-Output "With set -o equiv. `$ErrorActionPreference = 'Stop' : should return non-zero (false)" +$ErrorActionPreference = 'Stop' +Get-Content -path ./nofile | Write-Output "pipe statement after failure" +Write-Output "returns $?" ``` +./testfile.ps1 + ```output -PS> ./file.ps1 -Without `$PSNativeCommandErrorAction = 'Stop' : should return 0 (true) -cat: ./nofile: No such file or directory -pipe statement after failure -returns False +Without $ErrorActionPreference = 'Stop' : should return zero (true) +Get-Content: /Users/jasonhelmick/natcmdbash/testfile.ps1:2 +Line | + 2 | Get-Content -path ./nofile | Write-Output "pipe statement after failu … + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + | Cannot find path '/Users/jasonhelmick/natcmdbash/nofile' because it does not exist. -With set -o equiv. = 'Stop' : should return non-zero (false) -cat: ./nofile: No such file or directory -pipe statement after failure returns False +With set -o equiv. $ErrorActionPreference = 'Stop' : should return non-zero (false) +Get-Content: /Users/jasonhelmick/natcmdbash/testfile.ps1:6 +Line | + 6 | Get-Content -path ./nofile | Write-Output "pipe statement after failu … + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + | Cannot find path '/Users/jasonhelmick/natcmdbash/nofile' because it does not exist. ``` > [!NOTE] A common configuration command for POSIX shells `set -euo pipefail` includes the `set -u` @@ -277,13 +290,13 @@ The reported error record object will be the new type: `NativeCommandException` | Property | Definition ---------------- | ------------------- -| ExitCode: | The exit code of the failed command. -| ErrorID: | `"Program {0} ended with non-zero exit code {1}"`, with the command name and the exit code, from resource string `ProgramFailedToComplete`. -| ErrorCategory: | `ErrorCategory.NotSpecified`. +| ExitCode: | The exit code of the failed command. +| ErrorID: | `"Program {0} ended with non-zero exit code {1}"`, with the command name and the exit code, from resource string `ProgramFailedToComplete`. +| ErrorCategory: | `ErrorCategory.NotSpecified`. | object: | Exit code -| Source: | The full path to the application -| ProcessInfo | Details of failed command including path, exit code, and PID -| TargetObject | Specifies the object that was being processed when the error occurred. +| Source: | The full path to the application +| ProcessInfo | Details of failed command including path, exit code, and PID +| TargetObject | Specifies the object that was being processed when the error occurred. ## Alternative Approaches and Considerations From 84b263be8239e05ab6193464dd65a65c17c98829 Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Sat, 27 Mar 2021 10:59:22 -0700 Subject: [PATCH 15/16] changed $PSNativeCommandErrorAction to boolean --- .../RFC00XX-Native-Command-Error-Handling.md | 48 ++++--------------- 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index d859f8ff5..768a67a3c 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -189,7 +189,7 @@ Line | | Cannot find path '/Users/jasonhelmick/natcmdbash/nofile' because it does not exist. ``` -### set -o pipefail/ PSNativeCommandErrorAction +### set -o pipefail In the example below, PowerShell has no equivalent to `set -o pipefail` for native commands. @@ -257,32 +257,13 @@ Line | ### $PSNativeCommandErrorAction -The `$PSNativeCommandErrorAction` preference variable will implement a version of the -`$ErrorActionPreference` variable for native commands. +The `$PSNativeCommandErrorAction` preference variable will be implemented as a boolean. -- The value will default to `Continue` for compatibility with existing behavior. +- When enabled (1), `$ErrorActionPreference` will affect native commands with described behavior. +- When disabled (0), `$ErrorActionPreference` will have no effect to the original behavior. +- The default value is disabled (0) to maintain backward compatibility. - For non-zero exit codes and except for the value `Ignore`, an `ErrorRecord` will be added to `$Error` that wraps the exit code and the command executed that returned the exit code. -- Initially, only the existing values of `$ErrorActionPreference` will be supported in - `$PSNativeCommandErrorAction` as described in the table below. - -### Valid values for `$PSNativeCommandErrorAction` - -| Value | Definition ----------------- | ------------------- -| Break | Enter the debugger when an error occurs or when an exception is raised. -| Continue | Displays the error message and continues executing. -| Ignore | Suppresses the error message and continues to execute the command. -| Inquire | Displays the error message and asks you whether you want to continue. -| SilentlyContinue| The error message isn't displayed and execution continues without interruption. -| Stop | Displays the error message and stops executing. In addition to the error generated, the Stop value generates an ActionPreferenceStopException object to the error stream. stream -| Suspend | Automatically suspends a job to allow for further investigation. - -### Preference variable resolves error handling conflicts - -In cases where an existing script already handles non-zero native command errors, the preference -variable `$PSNativeCommandErrorAction` may be set to `Continue`. Error handling behavior of the -PowerShell cmdlets is handled separately with `$ErrorActionPreference`. ### Error object @@ -298,22 +279,13 @@ The reported error record object will be the new type: `NativeCommandException` | ProcessInfo | Details of failed command including path, exit code, and PID | TargetObject | Specifies the object that was being processed when the error occurred. -## Alternative Approaches and Considerations - -### Native commands should respect $ErrorActionPreference - -Native commands should use the $ErrorActionPreference setting and not need -$PSNativeCommandErrorAction. This could be released as an experimental feature. - -### Extending $PSNativeCommandErrorAction +### Preference variable resolves error handling conflicts -For users that prefer to set a single preference variable that affects both PowerShell and native -commands, The values of `$PSNativeCommandErrorAction` may be extended to include -`MatchErrorActionPreference`, which should apply the `$ErrorActionPreference` setting to native -commands. +In cases where an existing script already handles non-zero native command errors, the preference +variable `$PSNativeCommandErrorAction` may be set to `$false`. Error handling behavior of the +PowerShell cmdlets is handled separately with `$ErrorActionPreference`. -- If `$PSNativeCommandErrorAction` is set to `MatchErrorActionPreference` then the value of - `$ErrorActionPreference` will define the behavior for native command errors. +## Alternative Approaches and Considerations ### Explicit invocation logic From 6fb05cdcf7b1a9808844acd26dcfb7a0c458faa1 Mon Sep 17 00:00:00 2001 From: Jason Helmick Date: Thu, 1 Apr 2021 10:00:06 -0700 Subject: [PATCH 16/16] changed $PSNativeCommandErrorAction to $PSNativeCommandUseErrorActionPreference --- 1-Draft/RFC00XX-Native-Command-Error-Handling.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/1-Draft/RFC00XX-Native-Command-Error-Handling.md b/1-Draft/RFC00XX-Native-Command-Error-Handling.md index 768a67a3c..50b4c7825 100644 --- a/1-Draft/RFC00XX-Native-Command-Error-Handling.md +++ b/1-Draft/RFC00XX-Native-Command-Error-Handling.md @@ -255,9 +255,9 @@ Line | > equivalent to the existing PowerShell `Set-StrictMode` and does not need to be addressed in this > RFC. -### $PSNativeCommandErrorAction +### $PSNativeCommandUseErrorActionPreference -The `$PSNativeCommandErrorAction` preference variable will be implemented as a boolean. +The `$PSNativeCommandUseErrorActionPreference` preference variable will be implemented as a boolean. - When enabled (1), `$ErrorActionPreference` will affect native commands with described behavior. - When disabled (0), `$ErrorActionPreference` will have no effect to the original behavior. @@ -282,8 +282,8 @@ The reported error record object will be the new type: `NativeCommandException` ### Preference variable resolves error handling conflicts In cases where an existing script already handles non-zero native command errors, the preference -variable `$PSNativeCommandErrorAction` may be set to `$false`. Error handling behavior of the -PowerShell cmdlets is handled separately with `$ErrorActionPreference`. +variable `$PSNativeCommandUseErrorActionPreference` may be set to `$false`. Error handling behavior +of the PowerShell cmdlets is handled separately with `$ErrorActionPreference`. ## Alternative Approaches and Considerations