How does one extract a command's exit status into a variable?
Solution 1
Your command,
check=grep -ci 'text' file.sh
will be interpreted by the shell as "run the command -ci
with the arguments text
and file.sh
, and set the variable check
to the value grep
in its environment".
The shell stores the exit value of most recently executed command in the variable ?
. You can assign its value to one of your own variables like this:
grep -i 'PATTERN' file
check=$?
If you want to act on this value, you may either use your check
variable:
if [ "$check" -eq 0 ]; then
# do things for success
else
# do other things for failure
fi
or you could skip using a separate variable and having to inspect $?
all together:
if grep -q -i 'pattern' file; then
# do things (pattern was found)
else
# do other things (pattern was not found)
fi
(note the -q
, it instructs grep
to not output anything and to exit as soon as something matches; we aren't really interested in what matches here)
Or, if you just want to "do things" when the pattern is not found:
if ! grep -q -i 'pattern' file; then
# do things (pattern was not found)
fi
Saving $?
into another variable is only ever needed if you need to use it later, when the value in $?
has been overwritten, as in
mkdir "$dir"
err=$?
if [ "$err" -ne 0 ] && [ ! -d "$dir" ]; then
printf 'Error creating %s (error code %d)\n' "$dir" "$err" >&2
exit "$err"
fi
In the above code snippet, $?
will be overwritten by the result of the [ "$err" -ne 0 ] && [ ! -d "$dir" ]
test. Saving it here is really only necessary if we need to display it and use it with exit
.
Solution 2
You've told bash to set the variable check=grep
in the environment it passes to the command
-ci 'text' file.sh
but ci
does not exist.
I believe you meant to enclose that command in back-ticks, or in parentheses preceded by a dollar sign, either of which would assign the count of how many lines 'text' was found on (case insensitively) in the file:
check=`grep -ci 'text' file.sh`
check=$(grep -ci 'text' file.sh)
Now $check
should be 0 if no matches, or positive if there were any matches.
Solution 3
Your question is unclear but based on the code you’ve submitted, it looks like you want the variable check
to store the exit status of the grep
command. The way to do this is to run
grep -ci 'text' file.sh
check=$?
When running a command from a shell, its exit status is made available through the special shell parameter, $?
.
This is documented by POSIX (the standard for Unix-like operating systems) in its specification for the shell and the Bash implementation is documented under Special Parameters.
Since you’re a new learner, I’d strongly recommend that you start with a good book and/or online tutorial to get the basics. Recommendations of external resources are discouraged on Stack Exchange sites but I’d suggest Lhunath and GreyCat’s Bash Guide.
Solution 4
Other answers clarify why your command is not working, and some great suggestions as to how to test its result. I'd like to add one more.
$(command)$? appends the exit code
You can do this using a combination of command substitution and exit code checking, all in one line. It will append the exit code to the output as a number.
check=$( grep -ci 'text' file.sh )$?
This is actually $(some command)
(get the string output of a command) immediately followed by $?
(get the exit code of the last command). To work, they have to be used together with no space.
If there is any possibility of output, which is the case with most commands, you will have to suppress it using > /dev/null 2>&1
.
This may not be necessary if you're using a test command such as test -d <directory>
.
check=$( grep -ci 'text' file.sh > /dev/null 2>&1)$?
You could use a function to abstract this away:
e() {
$@ > /dev/null 2>&1
}
# Usage:
check=$(e grep -ci 'text' file.sh)$?
Testing the exit code
To use this later, test against zero (success):
[ $check -eq 0 ] && echo "passed" || echo "didn't pass"
[ $check -ne 0 ] && echo "didn't pass" || echo "passed"
Using an if/then/else construct, testing against zero (success):
if [ $check -eq 0 ]; then
echo "passed"
else
echo "didn't pass"
fi
if [ $check -ne 0 ]; then
echo "didn't pass"
else
echo "passed"
fi
Testing exit code directly
Using exit
within a (
subshell)
will produce a proper exit code from the number which you can use directly:
(exit $check) && echo "check passed" || echo "check didn't pass"
! (exit $check) && echo "check didn't pass" || echo "check passed"
Using an if/then/else construct, using exit code directly:
if (exit $check); then
echo "passed"
else
echo "didn't pass"
fi
if ! (exit $check); then
echo "didn't pass"
else
echo "passed"
fi
Using return
instead of exit
will also work in some shells - zsh included (but not bash).
Getting fancy
If you want to simplify even more, use this function:
t() { return $1; }
and now you can test like this:
t $check && echo "passed" || echo "didn't pass"
! t $check && echo "didn't pass" || echo "passed"
Solution 5
Confused why using -c when checking the output? It's used to check how many times something is matched - not if it's successful or not.
-c, --count
Suppress normal output; instead print a count of matching lines
for each input file. With the -v, --invert-match option (see
below), count non-matching lines. (-c is specified by POSIX.)
but in this example
check="$(grep --silent -i 'text' file.sh; echo $?)"
It doesn't output anything except a exit code, which is then echoed. This is the output that the variable check uses. I also prefer it because it's a single line.
You can replace --silent with -q. I use it since you're not interested in the grep output, just whether it worked or not.
-q, --quiet, --silent
Quiet; do not write anything to standard output. Exit
immediately with zero status if any match is found, even if an
error was detected. Also see the -s or --no-messages option.
(-q is specified by POSIX.)
$ check=$(echo test | grep -qi test; echo $?) # check variable is now set
$ echo $check
0
$ check=$(echo null | grep -qi test; echo $?)
$ echo $check
1
Related videos on Youtube
homes
Updated on September 18, 2022Comments
-
homes over 1 year
I have table A which has ID and proposalID. Table B has a proposalID as well. What I want to do is for each distinct proposalID in table B, I want to create a new row in table A with the same proposalID and a new ID.
So table A has these distinct proposalIDs:
94CAEF39-855B-4B5C-9534-9D4AD0A75FC8 C1D87317-7028-4F69-91D4-FFFBB35E7ACD 807D7733-5CCF-486F-81E5-FFF153307C22 521E22E2-511E-46F2-AA46-FFF832367A9E
What i want table B to have now is:
ID: new uniqueidentifier, proposalID: 94CAEF39-855B-4B5C-9534-9D4AD0A75FC8 ID: new uniqueidentifier, proposalID: C1D87317-7028-4F69-91D4-FFFBB35E7ACD ID: new uniqueidentifier, proposalID: 807D7733-5CCF-486F-81E5-FFF153307C22 ID: new uniqueidentifier, proposalID: 521E22E2-511E-46F2-AA46-FFF832367A9E
I'm still learning sql and don't know how to approach this. I know how to get all the distinct proposalIDs but after that i don't know.
-
tuzion almost 8 yearsFor exit status you can examine
$?
right after command is finished. -
Alessio almost 8 yearsand you can examine the PIPESTATUS array if you want the status of one of the commands in a pipeline.
-
-
Jakub Jindra over 3 years1)
check=(grep -ci 'text' file.sh)
creates array containing elements "grep", "-ci" "text" "file.sh".echo $check
will print only first element, to get all you needecho ${check[*]}
. 2)check=$(command)$?
will store output of command and append exit code. trycheck=$(echo "a b c")$?; echo $check
-
robrecord over 3 yearsAh, you're right! Will edit.
-
Kusalananda almost 3 yearsThe
-q
option forgrep
is standard, and always available. -
renatodamas almost 3 yearsYes I just meant that a silent mode is not available for other commands other than
grep
since what lead me here was a different command. -
Peeech over 2 yearsThis answer helps a lot in my case, where I use
set -e
in my bash script. With this setting checking exit status in a new line results in terminating the script in case of non-zero exit status of a command.$(command)$?
syntax works as expected. Thanks!