This question already has answers here:
Does $! mean something in shell scripting
(3 answers)
Closed 4 years ago.
I've been working on Shell and I've seen something like :
pid_A2=$!
wait $pid_A2
pid_A2=$?
Would you please explain the difference between the two syntax "$!" and "$?"...
Actually, i know that $? is the exit status of the previous command but I've never seen the previous one.
$?: the status of the last process execution
$!: the pid of the last command in background
$! is the last job of the background process. For example:
$ sleep 1000 &
[1] 6646 ---> process id
echo "$!" will print the process id of the last command (here, 6646).
$? returns the exit value of the command which is recently executed. $? is used when we want to handle the return value of a command or function. For example:
if [ **$?** -eq 1 ];
then
# do something
fi
Related
This question already has answers here:
How can I wait for certain output from a process then continue in Bash?
(5 answers)
Closed 1 year ago.
I'm executing a command in a bash shell and need to wait until a command echos a string. This is the pseudo code for what I have in mind. What do I set currentline to such that the until loop exits when the text output I'm waiting for echos?
currentline=""
until [[$currentline | grep -m 1 "the text output I'm waiting for"]]; do
echo " == Waiting for the text == "
currentline=???
sleep 1;
done
EDIT: the text output I'm waiting for tells the script the executed command is ready to receive input. The command does not terminate after the text output I'm waiting for. I want the script to stop sleeping and continue.
EDIT #2: The script runs on a remote CI/CD service, Codemagic, so I don't think I can use tail or any command that needs the path to the log file? (As described here)
So just:
while IFS= read -r line; do
if [[ "$line" == "the text .... " ]]; then
break;
fi
done < <(your-cmd)
if you want to keep your-cmd running, use coproc.
coproc your-cmd
while ..... as above ...
done <&"${COPROC[0]}"
I am new to shell script. I have a scenario where i need to get the exit status of one shell script and pass its value as input(if exit code is zero/otherwise exit the main shell script file) to the next shell script and continue until it executes all the scripts. Below is the code i tried.but its not working as expected.
status=`run1.sh`
if [[ status -eq 0 ]]; then
status=`run2.sh`
else
exit 1
fi
if [[ status -eq 0 ]];then
status=`run3.sh`
else
exit 2
fi
Its running successfully for first 2 scripts. It's failing on second if block, even though the output value of run2.sh is 0. I searched in google, its suggesting to use {PIPESTATUS[#]}, I tried it by replacing exit 1 with pipestatus and pass pipestatus in the second if block like below.
status=`run1.sh`
if [[ status -eq 0 ]]; then
status=`run2.sh`
else
exit ${PIPESTATUS[1]} ## pipestatus[1]- is run2.sh output value
fi
if [[ ${PIPESTATUS[1]} -eq 0 ]];then
status=`run3.sh`
else
exit 2
fi
I think i am not clear on how to use pipestatus. I would appreciate if anyone can provide me some example to my scenario.
status is a static string; you are not examining the variable you created at all.
There is no pipe here so PIPESTATUS does not come into play. It's for code like
one | two
where traditionally the exit status of one wasn't available to the shell; Bash changed that by exposing results from every process in a pipeline.
Having your scripts print the number zero to signal success is not how it's usually done. A command produces both output and a status code (the number you give as the argument to exit, or in a function, to return), and code would usually examine the latter. It is exposed in $? but the shell's control structures implicitly check it under the hood, so your code would look like
if run1.sh; then
if run2.sh; then
if run3.sh; then
...
provided you change them to produce a useful exit code. Though this could be further simplified to
run1.sh || exit
run2.sh || exit
run3.sh || exit
or even to
set -e
run1.sh
run2.sh
run3.sh
This question already has answers here:
What does set -e mean in a bash script?
(10 answers)
Closed 6 years ago.
In a bash script what is the use of
set -e
?
I expect it has something to do with environment variables but I have not come across it before
quoting from help set
-e Exit immediately if a command exits with a non-zero status.
i.e the script or shell would exit as soon as it encounters any command that exited with a non-0(failure) exit code.
Any command that fails would result in the shell exiting immediately.
As an example:
Open up a terminal and type the following:
$ set -e
$ grep abcd <<< "abc"
As soon you hit enter after grep command, the shell exits because grep exited with a non-0 status i.e it couldn't find regex abcd in text abc
Note: to unset this behavior use set +e.
man bash says
Exit immediately if a simple command (see SHELL GRAMMAR above) exits with a non-zero
status. The shell does not exit if the command that fails is part of the command list
immediately following a while or until keyword, part of the test in an if statement,
part of a && or ││ list, or if the command’s return value is being inverted via !. A
trap on ERR, if set, is executed before the shell exits.
It is super convenient way to get "fail-fast" behaviour if you want to avoid testing the return code of every command in a bash script.
Suppose there is no file named trumpet in the current directory below script :
#!/bin/bash
# demonstrates set -e
# set -e means exit immediately if a command exited with a non zero status
set -e
ls trumpet #no such file so $? is non-zero, hence the script aborts here
# you still get ls: cannot access trumpet: No such file or directory
echo "some other stuff" # will never be executed.
You may also combine the e with the x option like set -ex where :
-x Print commands and their arguments as they are executed.
This may help you debugging bash scripts.
Reference:Set Manpage
Editing this post, original is at bottom beneath the "Thanks!"
command='a.out arg1 arg2 &'
eval ${command}
if [ $? -ne 0 ]; then
printf "Command \'${command}\' failed\n"
exit 1
fi
wait
Here is a test script that demonstrates the problem, which I oversimplified
in the original post. Notice the ampersand in line 2 and the wait command.
These more faithfully represent my script. In case it matters, the ampersand
is sometimes there and sometimes not, its presence is determined by a user-
specified flag that indicates whether or not to background a long arithmetic
calculation. And, also in case it matters, I'm actually backgrounding many
(twelve) processes, i.e., ${command[0..11]}. I want the script to die if any
fail. I use 'wait' to synchronize the successful return of all processes.
Happy (sort of) to use another approach but this almost works.
The ampersand (for backgrounding) seems to cause the problem.
When ${command} omits the ampersand, the script runs as expected:
The executable a.out is not found, a complaint to that effect is issued,
and $? is non-zero so the host script exits. When ${command} includes
the ampersand, the same complaint is issued but $? is zero so the
script continues to execute. I want the script to die immediately when
a.out fails but how do I obtain the non-zero return value from a
backgrounded process?
Thanks!
(original post):
I have a bash script that uses commands of the form
eval ${command}
if [ $? -ne 0 ]; then
printf "Command ${command} failed"
exit 1
fi
where ${command} is a string of words, e.g., "a.out arg1 ... argn".
The problem is that the return code from eval (i.e., $?) is always
zero even when ${command} fails. Removing the "eval" from the above
snippet allows the correct return code ($?) to be returned and thus
halt the script. I need to keep my command string in a variable
(${command}) in order to manipulate it elsewhere, and simply running
${command} without the eval doesn't work well for other reasons. How do I catch the
correct return code when using eval?
Thanks!
Charlie
The ampersand (for backgrounding) seems to cause the problem.
That is correct.
The shell cannot know a command's exit code until the command completes. When you put a command in background, the shell does not wait for completion. Hence, it cannot know the (future) return status of the command in background.
This is documented in man bash:
If a command is terminated by the control operator &, the shell
executes the command in the background in a subshell. The shell does
not wait for the command to finish, and the return status is 0.
In other words, the return code after putting a command in background is always 0 because the shell cannot predict the future return code of a command that has not yet completed.
If you want to find the return status of commands in the background, you need to use the wait command.
Examples
The command false always sets a return status of 1:
$ false ; echo status=$?
status=1
Observe, though, what happens if we background it:
$ false & echo status=$?
[1] 4051
status=0
The status is 0 because the command was put in background and the shell cannot predict its future exit code. If we wait a few moments, we will see:
$
[1]+ Exit 1 false
Here, the shell is notifying us that the brackground task completed and its return status was just as it should be: 1.
In the above, we did not use eval. If we do, nothing changes:
$ eval 'false &' ; echo status=$?
[1] 4094
status=0
$
[1]+ Exit 1 false
If you do want the return status of a backgrounded command, use wait. For example, this shows how to capture the return status of false:
$ false & wait $!; echo status=$?
[1] 4613
[1]+ Exit 1 false
status=1
From the man page on my system:
eval [arg ...] The args are read and concatenated together into a single command. This command is then read and executed by the shell, and its exit status is returned as the value of eval. If there are no args, or only null arguments, eval returns 0.
If your system documentation is 'the same', then, most likely, whatever commands you are running are the problem, i.e. 'a.out' is returning '0' on exit instead of a non-zero value. Add appropriate 'exit return code' to your compiled program.
You might also try using $() which will 'run' your binary instead of 'evaluating' it..., i.e.
STATUS=$(a.out var var var)
As long on only one 'command' is in the stream, then the value of $? is the 'exit code'; otherwise, $? is the return code for the last command in a multi-command 'pipe'...
:)
Dale
This question already has answers here:
Meaning of $? (dollar question mark) in shell scripts
(8 answers)
Closed 4 years ago.
There is this line in a shell script i have seen:
grep -e ERROR ${LOG_DIR_PATH}/${LOG_NAME} > /dev/null
if [ $? -eq 0 ]
$? is the exit status of the most recently-executed command; by convention, 0 means success and anything else indicates failure. That line is testing whether the grep command succeeded.
The grep manpage states:
The exit status is 0 if selected lines are found, and 1 if not found. If an error occurred the exit status is 2. (Note: POSIX error handling code should check for '2' or greater.)
So in this case it's checking whether any ERROR lines were found.
It's checking the return value ($?) of grep. In this case it's comparing it to 0 (success).
Usually when you see something like this (checking the return value of grep) it's checking to see whether the particular string was detected. Although the redirect to /dev/null isn't necessary, the same thing can be accomplished using -q.
It is an extremely overused way to check for the success/failure of a command. Typically, the code snippet you give would be refactored as:
if grep -e ERROR ${LOG_DIR_PATH}/${LOG_NAME} > /dev/null; then
...
fi
(Although you can use 'grep -q' in some instances instead of redirecting to /dev/null, doing so is not portable. Many implementations of grep do not support the -q option, so your script may fail if you use it.)