Why echo $? returns different value than expected? - bash

I know that echo $? returns the exit status of the previous process.
In my program I get 0 if my program ends with a status code of 0. But when the program ends with status code -1 echo $? shows 255. Similar questions are there on StackOverflow, but is not explained why is it so. Plus one of the answers says generally values above 128 indicates some error and negative exit code. Like for example, 147 - we can get the real exit code by subtracting 128 from 147, 128 - 147 = -19, so the exit code was -19.
In my computer I find 256 as that number. For example 255 - 256 = -1, which was what my program was returning.
Why on different systems do these numbers vary 128, 256 and why can't they print the original negative code instead of adding these numbers to the original exit status code.

For historical reasons, exit code is interpreted as unsigned one-byte value. "Normal" error codes are positive (<127), while "abnormal" error codes (e.g. those produced by terminating on signal) are negative (>=128).
Try pressing Ctrl-C on a sleep 10 command. The exit code is 130.
127 is the result of "command not found".
It's really not a recommended practice to use negative error codes explicitly.
For more information, take a look at wait() man page (man 2 wait), in particular WFEXITED() and friends.
WEXITSTATUS(status)
returns the exit status of the child. This consists of the least significant 8 bits of the status argument that the
child specified in a call to exit(3) or _exit(2) or as the argument for a return statement in main(). This macro should
only be employed if WIFEXITED returned true.
(Emphasis added)

Related

valgrind --error-exitcode , negative values

I'm trying to set a custom error code if valgrind reports an error(building a test script), but it doesn't seem to work as expected when setting a negative value, this how I run valgrind:
valgrind --leak-check=full --error-exitcode=-1 -q ./leaks
This however, returns 0 (which is the return value of leaks), instead of -1.
echo $?
0
If a positive value is set, it works as expected:
valgrind --leak-check=full --error-exitcode=1 -q ./leaks
echo $?
1
The Valgrind manual, specifies:
--error-exitcode=<number> [default: 0]
Specifies an alternative exit code to return if Valgrind reported any errors in the run. When set to the default value (zero), the
return value from Valgrind will always be the return value of the
process being simulated. When set to a nonzero value, that value is
returned instead, if Valgrind detects any errors. This is useful for
using Valgrind as part of an automated test suite, since it makes it
easy to detect test cases for which Valgrind has reported errors, just
by inspecting return codes.
Anybody knows why this behavior occurs?
Using Valgrind-3.10.0
The manual is inaccurate, Valgrind can't return a negative value, it is in Valgrind's source. It assigns the option's value to clo_eror_exitcode:
else if VG_INT_CLO (arg, "--error-exitcode", VG_(clo_error_exitcode)) {}
and then checks if it is positive, otherwise it ignores the option.
if (VG_(clo_error_exitcode) > 0
&& VG_(get_n_errs_found)() > 0) {
VG_(client_exit)( VG_(clo_error_exitcode) );
} else {
/* otherwise, return the client's exit code, in the normal
way. */
VG_(client_exit)( VG_(threads)[tid].os_state.exitcode );
}
Even if it could, it would not be useful as per Linux, negative exit codes?

exit code: -1 on codeforces

I submitted a code on codeforces and on the 11th test case i got this-
Test: #11, time: 2000 ms., memory: 0 KB, exit code: -1, checker exit code: 0, verdict: TIME_LIMIT_EXCEEDED
Input
999999994 108004280
What does exit code: -1 signify here?
Seems like you should pay more attention to verdict: TIME_LIMIT_EXCEEDED, which means that your program did not fail but worked too long.
I suppose this exit code is just some default value for time limit exceeded cases and not something related to your code.

Exit Code when called with No Arguments

I have a script that requires at least one argument. If the arguments are incorrect I will obviously return a non-zero exit code.
I have decided to print the usage instructions when called with no arguments, but I'm uncertain of the exit code. Should it be
0 (success)
one of the values [1-125] (error)
An argument could be made either way. Is some option recommended?
It depends on whether you want the callers of your script to know programatically why it failed.
If, for example, it will only ever be run by a human, you could just return 1 and rely on the error text to inform them.
You could even return zero regardless if you only ever want a human to easily know (via the output) if it failed but I would consider this very bad form - Microsoft have made this mistake in the past with some of their commands not setting %errorlevel%, making it very difficult for a script to figure out whether it's worked or not.
Even if you decide not to indicate why it failed, you should at least indicate that it failed.
If you want a program to easily figure out why it failed, return a different code for each discernible error type.
Any Unix command when it returns control to its parent process returns a code, number between 0 and 255.
Success is traditionally represented with exit 0; failure is normally indicated with a non-zero exit-code. This value can indicate different reasons for failure.
So what you have also is another case of unsuccessful completion of your program which should be treated as returning a proper error code and not 0.
Also note that traditional Unix system calls don't signal an error code for empty arguments, they have to be caught explicitly. There are ones for argument list too long (E2BIG) and invalid arguments (EINVAL).
This is what I decided on using, based mostly on Inians answer and the reserved exit codes presented here.
I decided on somewhat reusing the exit codes defined in /usr/include/asm-generic/errno-base.h1. There was nothing suitable for "too little arguments", so I picked the first one from the 64-113 range.
# Error codes
E2BIG=7
EINVAL=22
E2SMALL=64
NAME="my-script"
USAGE=$(cat << END
the help text
RETURN CODES
$NAME has the following return codes:
0 success
$E2BIG Argument list too long
$EINVAL Invalid argument
$E2SMALL Argument list too short
END
)
# This script requires exactly one argument. If less are provided it prints
# the USAGE instructions
if [ ${#} -lt 1 ] ; then
printf '%s\n' "${USAGE}"
exit $E2SMALL
fi
if [ "${1}" = "-h" ] || [ "${1}" = "--help" ] ; then
printf '%s\n' "${USAGE}"
exit 0
fi
if [ ${#} -gt 1 ] ; then
printf '%s\n' "${USAGE}"
exit $E2BIG
fi
It's a bit verbose, but at least there's a properly defined error code if the script is called improperly.
1 This may be debatable, but since the codes were already defined...

Search in logfile - exit code

I have some legacy softwares that I need to automate under Control-M. These job are under Windows 2008R2.
These jobs have an exit code 0 if they run ok, but also if they can manage some errors. I need to raise an alarm when a specific string is in the log.
The string is not in the output of executable.
I implemented another job for this. It goes to search a string inthe file and in "On Do Actions" I search for the statement.
To have the statement in the output I think to use something like a grep. I used:
findstr
findstr "myerrorcode" D:\Log\greptest_%%$ODATE..log
grep under cygwin
In both case I have an identical situation:
If the string is found, everythings is ok
If the file is not found or cannot be open, grep or findstr return an exit code = 1. This is ok, because the job has to raise an error.
But the problem is: when the string is not found in the file, both grep and findstr have a return code = 1.
How can I discriminate the cases when the file cannot be open and when everything runs ok, but the sring in the log is not found?
You should be able to use grep's exit status to detect the reason for failure. According to the POSIX grep docs, exit status section:
EXIT STATUS
The following exit values shall be returned:
0 One or more lines were selected.
1 No lines were selected.
>1 An error occurred.
It's similar for GNU grep (consistent, but more specific):
Normally the exit status is 0 if a line is selected, 1 if no lines were selected, and 2 if an error occurred. [...] Other grep implementations may exit with status greater than 2 on error.
For example, in bash, you could use the case command to handle multiple branches like this:
#!/bin/bash
# search for error code in file
grep code file
# store the exit status in variable err
err=$?
# test several cases
case $err in
0) echo All good.;;
1) echo Code not found.;;
*) echo Error reading from file.;;
esac
You can handle this within Control-M easily:
Add in Job Tab "Action"
Add "On Do Action"
On: "Specific statement output"
Statement (as Control-M documentation state):
A character string, from 1 through 132 characters in length, containing a statement from the job script file The specified string can be a portion of the statement.
Statement character strings can each contain mask characters. Valid
mask characters are:
* – represents any number of characters (including no characters)
$ – represents any single character
? – represents any single character
Code:
A character string, from 1 through 255 characters in length, to be
compared to the operating system’s response to the specified
statement.
Code character strings can each contain mask characters. Valid mask
characters are:
* – represents any number of characters (including no characters)
$ – represents any single character
? – represents any single character
Example:
On Do Action Control-M 8

How to return from shell script with return value 1 with successful completion of script

How to return from shell script with return value 1 with successful completion of script?
Firstly, returning a value of 1 to indicate success is exactly the opposite of expected behavior, so you really should not do it. However, if you want to, then just do
exit 1
However, this typically indicates failure, and you would do well to respect the convention.
0 indicates success, non-zero indicates failure.
You should use exit or exit 0 in case of success. In case of failure, use exit 1. If you want to return information why your program has failed, you can use several different return values, i. e. 1, 2, 3, -1, 255, etc.

Resources