In tcsh how do you get the exit status of a command in backquotes? - tcsh

I have the following code in my tcsh startup script:
set _color_count = `sh -c "tput -T${TERM}-256color colors 2>/dev/null"`
if ($? == 0) then # check if it was a valid terminal type
if ($_color_count != 256) then # sanity-check
echo "Warning: Color count '$_color_count' for '${TERM}-256color' is not 256"
endif
setenv TERM "${TERM}-256color"
endif
My problem is that the exit status ($?) is always zero, even when the tput command returns a non-zero exit status due to an invalid terminal type. If I don't capture the output of the command, checking the exit status works fine:
sh -c "tput -T${TERM}-256color colors 2>/dev/null"
How do I determine whether or not the tput command returned a non-zero exit status, given it's in backquotes?

It turns out that this was a behavioral change introduced in tcsh version 6.17.05 (see original bug report). It looks like it will be reverted as of tcsh version 6.18.00 (see regression bug report), but it has obviously made it into the wild.
For affected versions, though, you can set the variable $anyerror before running the command in backquotes:
set anyerror
set _color_count = `sh -c "tput -T${TERM}-256color colors 2>/dev/null"`
According to my tcsh man page for special shell variable status:
status The status returned by the last command, unless the variable anyerror is set, and any error in a pipeline or a backquote expansion will be propagated (this was the default csh behavior).

Related

Bash exit status not caught despite set -e and/or trap being active

Can someone explain the bash/set -e behaviour on the code snippet below ?
#!/bin/bash
# Comment if you want to test the trap only
set -e -o pipefail -u -E
# Comment if you want to test the set -e only
trap "echo ERROR CAUGHT; exit 12" ERR
function reproduce() {
# Trigger arithmetic error on purpose
a=$((1109962735 - hello=12272 + 1))
}
reproduce
# The script is expected to trigger the trap and/or activate the set -e. In both cases it should stop and exit here on error.
status_code=$?
echo "STATUS ${status_code}"
if [[ "${status_code}" != "0" ]];then
echo "FIXME: why was status code not caught by set -e ?"
echo "Executing false to prove set -e is still active"
false
# If the following is not executed then it proves -e is still active
echo "set -e not active !!!!!"
exit 2
fi
Here is what is obtained when executing it:
$ bash reproduce.sh
reproduce.sh: line 8: 1109962735 - hello=12272 + 1: attempted assignment to non-variable (error token is "=12272 + 1")
STATUS 1
FIXME: why was status code it not caught by set -e ?
Executing false to prove set -e is still active
ERROR CAUGHT
Checking the exit code
$ echo $?
1
Bash version
bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Reproduced as well with
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
Additional notes, related to comments (thanks to all suggestions anyway):
Commenting trap does not change the weird behaviour observed
Removing set -e to only keep the trap does trigger the trap either
Let's simplify it; minimum amount of code required for
reproducing the issue you're dealing with is
set -e
: $((+)) # arithmetic expansion error
echo survived
According to the standard, this should never print survived, it says a POSIX shell running non-interactively shall immediately exit upon an expansion error. But seemingly Bash doesn't think so. Although this difference isn't explicitly documented in the man page, in description of POSIX mode it says
Non-interactive shells exit if a syntax error in an arithmetic
expansion results in an invalid expression.
We can say this means in its default operating mode, a non-interactive Bash session doesn't exit upon such error, but as you realized, it doesn't trigger errexit mechanism, or ERR trap either. Instead, it assigns a non-zero value to $? an moves on.
To overcome this and get the expected behavior, you should define reproduce as follows
function reproduce() (
# Trigger arithmetic error on purpose
a=$((1109962735 - hello=12272 + 1))
)
This way the expansion error will take place in a subshell and cause it to exit with a non-zero status, thus, errexit and trap will be able to catch it.
Upon dash-o's request, here is a version that sets a for the current execution environment when the expression is valid
function reproduce() {
if : $((expression)); then
a=$((expression))
fi
}
On surface, it looks as if bash will not trigger the trap on various SYNTAX errors. Only when it a command (external, built-in) is executed (and return non-zero), the ERR trap will be trigered.
From the man page:
If a sigspec is ERR, the command arg is executed whenever a pipeline
(which may consist of a single simple command), a list, or a compound
command returns a non-zero exit status, subject to the following
conditions ...
The ERR trap only applies to PIPELINE. If bash identifies a syntax error, it aborts before executing the pipeline, therefore NO trap. Even though he documentation for '-e' specify the same condition (if a pipeline ... exit with non-zero status), the observed behavior is different.
If you try other expansions - e.g. command expansion- trap is triggered, as there is pipeline execution:
a=$(bad-commands)
a=$([)
If use try various syntax errors in arithmetic expansion, trap not triggered - there was no pipeline.
a=$((2+))
a=$((2 #))
Also, other bash Syntax error do not trigger the trap: (), [[ ]].
I could not find a solution that does not require extensive changes to the source script. May be file a bug/feature request with the bash team ?

Shell/Bash: how to fail on just undefined functions in command-line shell?

I want to make sure my command-line shell auto fails on just undefined functions.
I do not want my shell scripts to fail on undefined variables since that screws up some external shell scripts that I am sourcing in my .bashrc (Particularly .git-completion.bash starts to misbehave with set -u flag)
I also do not want to fail on return code 1 since that would make my shell unusable (grepping for something that doesn't exist in a file would auto exit my shell).
What I do want is hard stop anytime there is a function used that is undefined. Ideally process interrupt like kill -INT -$$ which would stop the current process right than and there but would not exit the shell, but just exiting the shell would work as well as long as it only works on undefined functions and also works when output of the functions is captured.
What I mean by works with output of functions is captured is just setting set -u, set -e doesn't even fully get the job done:
go1(){
set -u
set -e
iDontExist
echo "!!! reached operation after undefined !!!"
}
# > go1
# bash: iDontExist: command not found
# Saving session...bash: HISTTIMEFORMAT: unbound variable
The above works. However if I capture the output the flags do not catch the undefined error (even though I don't even want to use these flags due to the reasons specified at the beginning of the question)
go2(){
set -u
set -e
set -o pipefail
echo $(iDontExist)
echo "!!! reached operation after undefined !!!"
}
# > go2
# bash: iDontExist: command not found
#
# !!! reached operation after undefined !!!
Is there some other awesome flag that would get the job done?
When using pipefail and errorexit you want to either clear the error status with:
may_fail_command || true
or either deal with the return status in a condition
declare myvar
if ! myvar="$(may_fail_command)"; then
printf 'Error assigning myvar because may_fail_command failed code %d\n' $?
fi
If you have to source file with possible errors, you can temporarily disable the errorexit nounset pipefail flags.
You should also test the return code of your sourcing
if ! source may_fail_sourced.sh; then
... handle sourcing errors
fi
if I capture the output
Well, there is a better way to capture it. The echo $(...) statement has the exit status of echo command, not the $(...), so it will never trigger set -e. Do:
go2(){
set -u
set -e
set -o pipefail
str=$(iDontExist)
echo "$str"
echo "!!! reached operation after undefined !!!"
}
go2
This should be fixed with Git 2.32 (Q2 2021): the command line completion (in contrib/) is safer with "set -u".
See commit 76655e8 (16 Apr 2021) by Ville Skyttä (scop).
(Merged by Junio C Hamano -- gitster -- in commit 5980e0d, 30 Apr 2021)
completion: avoid aliased command lookup error in nounset mode
Signed-off-by: Ville Skyttä
Aliased command lookup accesses the list variable before it has been set, causing an error in "nounset" mode.
Initialize to an empty string to avoid that.
$ git nonexistent-command <Tab>bash: list: unbound variable

How to catch /bin/bash: bad interpreter error

recently came across an issue when running a bash script executed in a csh shell. This was outputed: /bin/bash: bad interpreter: No such file or directory. The problem was bash was not on the environment path. After adding bash, this was fixed. I want to make sure that in the future, if this ever happened again for some reason, I can handle this. I am wonder what exit code this is? or is this just printed out on stderr? I want to catch this and fail the main script. Any ideas on how to handle this?
I have this segment:
bash sc142.sh
#####################################################################
# Check for processing errors
#####################################################################
if ($status != 0) then
exit (-1)
endif
I tried this on Debian, the exit status for a bad interpreter error is 126. So you can do:
/path/to/scriptname arg ...
if ( $status == 126 ) then
echo "scriptname failed"
exit 1
endif
Note that a false positive is possible. If the last command in the script you're running exits with status 126, you won't be able to tell the difference.
The exit code will be non-zero. The exact exit code depends on the environment. You may get 127 (command not found) but you may also get another non-zero exit code in certain shells.
In your csh script you can set the -e option which will cause the script to exit immediately if any commands fail.
#!/bin/csh -e
false
echo not printed

Why do bash "set -e" cause exit after a seeming error-free command?

Without using set -e, the script runs as expected, with all results correctly generated.
After adding set -e, it exits after this command:
./NameOfATool > result.txt
When I wrap set +e and set -e around that command, then the script terminates as expected.
Why would it exit, or what might be wrong with the command?
p.s. NameOfAToolis an executable compiled from C code. When I manually type that command, it runs ok without giving an error.
set -e will cause the script to exit if any command returns a non-zero exit status. (Well, there are a bunch of exceptions, but that's the general rule.) So, ./NameOfATool apparently returns a non-zero exit status. This might mean that it actually thinks there's an error, or it might mean that the program was poorly written and doesn't report an appropriate exit status for success, or it might mean that it uses special exit-status values to report specific things (much like the standard utility diff, which returns 0 for "same", 1 for "different", and 2 for "error").
Try set +e in your trap:
set -e;
trap 'x=$?; set +e; echo Hello; false; echo World; exit 22;' ERR
echo Testing
false
echo Never See This
Omit the set +e and you don't see the "World" as the non-zero exit code in the trap exits before the trap is completed.
As #ruakh said, this indicates that the tool is exiting with a nonzero (=error) status. You can prevent this from exiting the script by putting it in a compound command that always succeeds:
./NameOfATool > result.txt || true
If the tool exits with a nonzero status, it runs true, which succeeds; hence, the entire compound command is considered to have succeeded. If the command's exit status is significant (i.e. you need to be able to tell if it exited with status 0, 1, or 2), you can either record it for later use:
./NameOfATool > result.txt && toolStatus=0 || toolStatus=$?
...or use the status directly:
if ./NameOfATool > result.txt; then
# do things appropriate for exit status = 0
else
toolStatus=$?
# do things appropriate for exit status != 0
fi

exit status in shell script

I am running a application form the shell script. Now I like to know the exit status of the application to know whether it exit normally or abnormally ( crash etc). How I know it?
Example:
./mytestApp
Bash stores the last process' exit value in the special variable $?.
You can use special variable $? which contains an exit status of the last comand.
$? contains the exit status of the last command executed. So, if the last command was ./mytestapp, $? would contain its exit status immediately after (note that you can only retrieve this value once and it must be retrieved immediately after the command whose exit status you want to know). You may want to capture it in a variable, e.g.
#!/bin/bash
./mytestapp
APPSUCCESS=$?
# Continue doing whatever it is you're doing
This all assumes that you're using bash (sh and zsh will work as well, IIRC).
The special variable $? will contain the exit status of the last command in bash.

Resources