How to keep last exit status after test
Solution 1
$_
will work in (at least) interactive dash
, bash
, zsh
, ksh
(though apparently not in a conditional statement as requested) and mksh
shells. Of those - to my knowledge - only bash
and zsh
will also populate it in a scripted shell. It is not a POSIX parameter - but is fairly portable to any modern, interactive shell.
For a more portable solution you can do:
command -p sudo ...
eval '[ "$?" = 127 ] || exit '"$?"
Which basically allows you to expand the initial value for $?
into the tail of the script before ever even testing its value at its head.
But anyway, since you appear to be testing whether or not the command sudo
can be found in the shell's builtin -p
portable path string with command
, I would think you could go at it a litte more directly. Also, just to be clear, command
won't test for the location of any arguments to sudo
- so it is only sudo
- and nothing it invokes - which is relevant to that return value.
And so anyway, if that is what you're trying to do:
command -pv sudo >/dev/null || handle_it
command -p sudo something or another
...would work just fine as a test without any chance of errors in the command sudo
runs returning in such a way that might skew the results of your test.
Solution 2
There are various options to handle the exit status reliably without overhead, depending on the actual requirements.
You can save the exit status using a variable:
command -p sudo ...
rc=$?
[ "$rc" -ne 1 ] && echo "$rc"
You can directly check success or failure:
if command -p sudo ...
then echo success
else echo failure
fi
Or use a case
construct to differentiate the exit status:
command -p sudo ...
case $? in
(1) ... ;;
(127) ... ;;
(*) echo $? ;;
esac
with the special case asked in the question:
command -p sudo ...
case $? in (1) :;; (*) echo $?;; esac
All those options have the advantage that they are conforming to the POSIX standard.
(Note: For illustration I used echo
commands above; replace by appropriate exit
statements to match the requested function.)
Solution 3
command -p ...
test 1 -ne $? && exit $_
Use $_
, which expands to the last argument of the previous command.
Solution 4
You can define (and use) a shell function:
check_exit_status()
{
[ "$1" -ne 1 ] && exit "$1"
}
Then
command -p sudo ...
check_exit_status "$?"
Arguably, this is "cheating",
since it makes a copy of $?
in the check_exit_status
argument list.
This may seem a little awkward, and it is.
(That sometimes happens when you impose arbitrary constraints on problems.)
This may seem inflexible, but it isn't.
You can make check_exit_status
more complex,
adding arguments that tell it what test(s) to do on the exit status value.
Related videos on Youtube
serhatg
Updated on September 18, 2022Comments
-
serhatg almost 2 years
Is it possible to keep the last command exit status (
$?
) unaltered after a test?E.g., I would like to do:
command -p sudo ... [ $? -ne 1 ] && exit $?
The last
exit $?
should return the sudo exit status, but instead it always returns0
(the exit code of the test).Is it possible to do that without a temporary variable?
Another example to clarify further:
spd-say "$@" [ $? -ne 127 ] && exit $?
In this case i want to exit only if the first command is found (exit code !=
127
). And i want to exit with the actualspd-say
exit code (it may not be0
).EDIT: I forgot to mention that i prefer a POSIX-complaint solution for better portability.
I use this construct in scripts where i want to provide alternatives for the same command. For instance, see my crc32 script.
The problem with temporary variables is that they could shadow other variables, and to avoid that you must use long names, which is not good for code readability.
-
serhatg about 9 yearsthis is tricky, concise, and the
$_
variable seems POSIX-compliant, so i definitively like it. Just change the first line to avoid confusion.. -
Dan Cornilescu about 9 yearsValid for this particular example, but only usable if there are no other command in between the
$?
and$_
references. IMHO it's better to stick to a consistent method which works in other cases (and can also help with the code readability). -
Janis about 9 yearsAre you sure it's POSIX? - I doubt it. - It doesn't work, e.g., with
ksh
, since$_
is the last argument of the previous command that's not in a conditional command sequence. - In any case it's non-portable. -
Janis about 9 yearsConfirmed; it is not in the POSIX standard (see chapter 2.5.2, "Special Parameters"). (And in the ksh reference book it's also described as "ksh-feature not in POSIX".) Thus non-standard, not portable. I wouldn't use it.
-
llua about 9 yearsThe shell was specifically named twice, once in the title and once as a tag. Nor was portability mentioned anywhere in the question, thus i gave a answer that works in said shell. What other weird restrictions are going to be made up?
-
Jerb about 9 years@mikeserv I'm not sure I understand - are you talking about manually assigning to
$?
or something like that? -
Janis about 9 years@llua; You completely missed the obvious point. There was a very first comment here about POSIX, and I explained that this statement was wrong. I think giving such wrong impressions should be corrected to prevent spreading suggestions based on wrong facts. - Feel free to provide specifc bash solutions. And the poster may of course use what he likes. - Elsethread I said: "It's beyond me why he prefers complicated to simple methods.", and I emphasize here "It's beyond me why one would prefer non-portable tricks if keeping a widely usable solution is so simply." - YMMV.
-
mikeserv about 9 years@Janis - well, in
bash
anyway, the behavior for$_
is well-defined, at least. The same is not true for your own answer, the behavior of which can be easily altered by shell expansion side-effects.$_
does not have that liability. -
Janis about 9 years@mikeserv; The expansion side effects in the
case
patterns, the only place that would matter in theory (but not in the given question), is a constructed case. - In any case, your shell command substitution would propagate the result of the embedded command, usually other expansions will not affect the return state anyway if there's no commands involved that can create errors (mindx=${a!b}
cases, but irrelevant here). - What do you mean by "command without command name"? -
Janis about 9 years@mikeserv; It can be clobbered only by ad hoc made up unnecessary code. There's absolutely no grey area if you take the suggestion without unnecessarily introducing artificial nonsense. The requirements were absolutely clear in this case: 1. execute a comand, 2. check exit code, 3. return exit code. - Do you get that? - You changed that requirement arbitrarily to just make up an argument.
-
mikeserv about 9 years@Janis - Call it what you want, but in shell, people sometimes do use command substitutions to produce a value. It happens. And so if there were some command i could run to print an errno for which i needed to test, and i included that in my case pattern in a command substitution, fully confident that the answer given here by you would surely satisfy all of my expectations and more, i believe i would be sorely disappointed with the results. That is, i would be when i finally noticed and eventually tracked the inconsistent returns of my script. And provided it worked at all in the first place.
-
terdon about 9 yearsComments are not for extended discussion; this conversation has been moved to chat.
-
Janis about 9 years(I wonder why the last comment did not move to chat.) - As already thoroghly explained and backed up by POSIX quotes in the chat discussion (see there for details); 1.)
("$(get_errnos)")
is an artificial addition of a command substitution where comparisons with actual error codes are asked for in the question, 2.) There's clear in the standard what changes$?
(and thus what doesn't change it), and 3.) there are no side effects with that construct (unless, as you did, you introduce them unnecessarily). - OTOH, since you mind, the other answer that you prefer is clearly non-standard. -
serhatg about 9 yearssorry for messing with POSIX-compilancy (and for quoting bash in the title), this won't be my favourite answer then.
-
Janis about 9 years@mikeserv; It's annoying (and misleading!) not only because you apply double standards! If you'd apply the same artificial
$(get_errnos)
code to any other solutions (( exit 42 ); test "$(get_errnos)" -ne $? && echo $_
) they also don't work. (You preferred to bring my standard solution in miscredit, not the other non-standard hack(s).) - Of course you can add arbitrary code to any answers and spoil it. - And WRT to the change of$?
; just because you can't find it doesn't mean it's not true. (I've already provided in our discussions even the keywords to search for in POSIX.) -
Janis about 9 years@mikeserv; But this is, again, prone to continue the (fruitless and endless) discussion, that should not be placed here as @terdon correctly observed.
-
mikeserv about 9 yearsThat's a fair point - the first one. The second - well, I don't recall that you did. I would think I'd remember it. The first point, though, still doesn't answer to the
case
not working at all indash
,zsh
. And I really don't believe there is any such standard - I've combed it in the past - fine-toothed style - and I would probably remember that too. -
Janis about 9 years@mikeserv; I said quite at the beginning where you first mentioned
zsh
(first mentioned alone; later you added alsodash
) that I think it must be a bug there, given that thecase
exit status and setting of$?
seems well defined in POSIX. - And also here, again, you apply double standards; the other hack, e.g., is neither standard, nor does it reliably work in other standard shells (e.g. not inksh
). - My proposals are standard and work inbash
(mostly used on Linux) andksh
(the predominating shell in commercial Unixes). -
mikeserv about 9 yearsNo, it was not mentioned alone.
dash
, by the way, is the standard/bin/sh
on Debians - which is (unfortunately) also the most widely used Linux distribution variety out there. I don't want to argue about it - it's not that important. I just wish the answer could be better - as is, it's kinduva lie.