Skip to content

Commit e622135

Browse files
committed
Initial RFC Native Command Exit Error
follow-up to comittee review of pull request #3523 for issue #3415
1 parent c9dde98 commit e622135

File tree

1 file changed

+236
-0
lines changed

1 file changed

+236
-0
lines changed
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
---
2+
RFC: RFCNNNN-RFC-Native-Command-Exit-Errors
3+
Author: Micheal Padden
4+
Status: Draft
5+
Area: Engine, Language
6+
Version: 1.0.0
7+
---
8+
9+
# Native Command Error Handling
10+
11+
Although Powershell has an exception-based error handling framework, it
12+
does not currently apply to native commands.
13+
14+
Exception-based error handling makes it easier to write robust code
15+
as less boilerplate code is needed to check and handle errors.
16+
17+
Powershell scripts using native commands would benefit from being able
18+
to error handling features like those used by cmdlets.
19+
20+
## Motivation
21+
22+
Native commands return an exit code to the calling application which
23+
will be zero for success or non-zero for failure. A robust script will
24+
not assume success, instead checking the exit code after any statement
25+
calling a native command, using boilerplate like the following:
26+
```Powershell
27+
if( ! $? )
28+
{
29+
exit $lastexitcode;
30+
}
31+
```
32+
33+
Bourne type shells provide a `set -e` error handling mode that acts as
34+
if this boilerplate were included in certain contexts. Like exception
35+
based error handling, this can exit a stack of functions, scripts and
36+
shells. This allows robust scripts to be written with a minimal amount
37+
of boilerplate.
38+
39+
To support a similar style of error handling with native commands, this
40+
RFC proposes including native command errors in Powershell's exception
41+
handling framework in a similar way.
42+
43+
The specification and alternative proposals are based on the
44+
[Equivalent of bash `set -e` #3415](https://github.com/PowerShell/PowerShell/issues/3415)
45+
and committee review of the associated
46+
[pull request](https://github.com/PowerShell/PowerShell/pull/3523).
47+
48+
49+
## Specification
50+
51+
This RFC proposes including native commands in the error handling
52+
framework, by allowing an error to be reported to the error stream
53+
when a native command exits with a non-zero exit code, similar to
54+
the `set -e` option in bourne type shells.
55+
56+
The `$PSIncludeNativeCommandInErrorActionPreference` preference
57+
variable should govern treatment of non-zero exit codes on native
58+
commands. Possible values are:
59+
- `$false`: (the default) ignore non-zero exit codes.
60+
The effect is the same as existing Powershell treatment of this case.
61+
- `$true`: report an error for non-zero exit codes on a native command.
62+
63+
The reported error record should be created with the following details:
64+
- exception: `ExitException`, with the exit code of the failed command.
65+
- error id: `"Program {0} failed with exit code {1}"`, with the command
66+
name and the exit code, from resource string `ProgramFailedToComplete`.
67+
- error category: `ErrorCategory.NotSpecified`.
68+
- object: the (boxed) exit code.
69+
70+
The existing `$ErrorActionPreference` variable should govern how native
71+
command errors are handled. The `$ErrorActionPreference` value `"stop"`
72+
will treat such errors as terminating errors, allowing the error to
73+
terminate the session, or be caught as an exception.
74+
75+
Alternative approaches outlined below try to address limitations in the
76+
approach taken with this base specification.
77+
78+
## Alternate Approaches and Considerations
79+
80+
### Control native command error management with ActionPreference value
81+
82+
Another preference option could be added to `$ErrorActionPreference`.
83+
84+
With this approach, an `$ErrorActionPreference` value of
85+
`"StopIncludingNativeCommand"` would cause an exception to be thrown
86+
for non-zero exit codes on native commands, as well as any error in
87+
cmdlets.
88+
89+
This approach is limited to managing errors in native commands when
90+
this option is set, so integrates less well with overall Powershell
91+
error handling features. For example, where the preference is "Inquire",
92+
a non-zero exit code on a native command would not generate an inquiry.
93+
94+
### Treat non-zero exit codes as errors only on untested native commands
95+
96+
A similar approach that ignores the exit code in native commands where
97+
the output is tested should give useful results.
98+
99+
The `$PSManageNativeCommandErrors` preference variable would govern
100+
treatment of non-zero exit codes for "untested" native commands.
101+
Possible values would be:
102+
- `$false`: (the default) value would ignore any non-zero exit codes.
103+
- `$true`: would report and error for non-zero exit codes on an
104+
untested native command.
105+
Commands would be considered "tested" if used in the lexical scope of
106+
an `if` or loop condition, the operand of a logical operator
107+
(`!`,`||` or `&&`).
108+
109+
This should improve on the basic specification by allowing the idiom to
110+
be combined with normal control flow statement usage.
111+
112+
A common idiom with native commands is to return success or failure in
113+
the exit code rather than the output stream. Constructs such as if and
114+
while test the exit code rather than the command output in Bourne type
115+
shells, so treating non-zero exit code as an exception in such contexts
116+
would not make sense. Thus `set -e` is applied to "untested" commands
117+
only, where a "tested" command is any command in the scope of an `if`
118+
or `while` condition, a logical operator (`!`,`||` or `&&`) or any
119+
command before the last in a pipeline (see output of pipeline below).
120+
121+
Although PowerShell `if` and loop conditions check the output of the
122+
command rather than checking the exit code, a native command may be
123+
able to accept arguments that produce suitable output only on success,
124+
but still indicate the failure reason using the exit code. Where the
125+
native command does not provide such an option, it is probably not
126+
useful to test it's output in an `if` or loop condition.
127+
128+
Note: with bourne type shells, "tested" commands are those in the
129+
dynamic scope of a tested context rather than the lexical scope of the
130+
context. This limits the usefulness of `set -e`, but is needed for
131+
[historic compatibility](http://austingroupbugs.net/view.php?id=52)
132+
reasons.
133+
134+
### Treat errors as exceptions only on untested commands
135+
136+
This approach varies the basic specification so that errors in all
137+
commands can be treated exceptions where the command result is not
138+
tested.
139+
140+
As well as the new boolean preference to enable reporting errors for
141+
non-zero native command exit codes, a new preference value option would
142+
be added for `$ErrorActionPreference`.
143+
144+
A `"StopOnDiscarded"` value for `$ErrorActionPreference` would have the
145+
effect of ignoring non-terminating command errors in a context where
146+
the result is tested, and throwing an exception in other contexts.
147+
148+
This should improve on the basic specification by allowing the idiom to
149+
be combined with normal control flow statement usage and applied to
150+
cmdlets also.
151+
152+
Treating non-zero exit codes on native commands as errors only in
153+
untested contexts as with earlier listed approaches gives inconsitent
154+
error handling between native commands and cmdlets. The parameter
155+
`-ErrorAction SilentlyContinue` would need to be given to a cmdlet to
156+
prevent an exception being thrown where `$ErrorActionPreference` is
157+
`"stop"` and a non-terminating error should not cause an exception
158+
because the output is being tested.
159+
160+
### Allow output of pipeline to be treated as tested
161+
162+
This approach varies the above approaches to "untested" commands so
163+
that the output of a pipeline can be treated as tested.
164+
165+
A `$PSIncludeNativeCommandInErrorActionPreference` variable with three
166+
possible values would be used instead of a boolean variable.
167+
Possible values would be:
168+
`"none"`:(default) do not report an error on a non-zero exit code
169+
`"command"`: report an error on a non-zero exit code after a
170+
native command in an untested pipeline.
171+
`"pipeline"`: report an error on a non-zero exit code after an
172+
native command which is the last command in an untested pipeline.
173+
174+
This should add some flexibility for handling errors in multiple
175+
command pipelines to deal with different expectations.
176+
177+
In Powershell, the status of a pipeline is false if any command in the
178+
pipeline fails. Thus treating a command that outputs to a pipeline as
179+
"untested" is arguably be closer to this existing Powershell idiom.
180+
Korn compatible shells like bash provide similar behaviour with
181+
`set -o pipefail`. This makes the exit code of the pipeline zero where
182+
all exit codes were zero, or otherwise the exit code of the last
183+
command with a non-zero exit code.
184+
185+
However this might not be the expected behaviour in all circumstances,
186+
as `set -o pipefail` is not enabled on bourne type shells, and commands
187+
that output into a pipe might sometimes be considered to have their
188+
success or failure "tested" for error handling purposes.
189+
190+
### Use dynamic scope/Set-StrictMode
191+
192+
This approach implements dynamic scoping for native command error
193+
management using a cmdlet.
194+
195+
One of the approaches above would be enabled with
196+
`Set-StrictMode -version 6`, instead of a boolean preference variable.
197+
For example,
198+
- An error would be eported for native commands with a non-zero exit
199+
code.
200+
- An exception would be thrown for "untested" native commands with a
201+
non-zero exit code.
202+
203+
The dynamic scoping approach would improve on earlier listed approaches
204+
by limiting the scope of error handling configuration so that script
205+
functions could not have an effect on the error handling mode in the
206+
calling scope.
207+
208+
There might be difficulties using `Set-StrictMode` for this. Other
209+
strict mode checks tend to identify coding errors based on the internal
210+
Powershell context, not than the wider OS native environment. Use of
211+
the latest strict mode with existing scripts that already implement
212+
robust error handling on native commands would be more difficult, as
213+
normal control flow statements involving native commands would have to
214+
be restuctured into `try`/`catch` statements.
215+
216+
The dynamic scoping approach would have side-effects on called scripts.
217+
218+
### Use lexical scope/Exception handling extensions
219+
220+
With the lexical scoping approach, native command error handling mode
221+
would be used through language syntax instead of preference variables
222+
or cmdlets. A possible syntax might be to add parameters to the try
223+
statement.
224+
225+
For example:
226+
- `WithNativeCommandExitError` parameter, to report an error where a
227+
native command exits with a non-zero exit code
228+
- `WithIgnoredErrorException` parameter, to throw an exception for
229+
reported command errors where the result of the command is not tested.
230+
231+
Lexical scoping of native command error handling would improve on
232+
earlier listed approaches in a number of ways. Native command error
233+
handling would sit alongside overall exception handling. There would be
234+
no side-effects in calling or in called scripts. It could also minimize
235+
runtime overhead as the facility should be mostly handled by the parser
236+
and compiler.

0 commit comments

Comments
 (0)