Bash: is there a way to execute a command if another command times out? - bash

I need to run a command with the timeout function, so that if it is not executed in x time, then the script shall start from the beginning, otherwise it can proceed and execute other commands. Can I do that?
I already tried to use case, but it works as long as I use one, if use two cases it gives me an error. Does anybody know how to do it with timeout? Maybe using an user-defined signal like -s USR1, but I don't know how to set that up and if I can execute a command with a signal :/

If some_command completes within 10 seconds, this will continue on with the rest of the script. If some_command times out, then it is repeated until it doesn't time out:
while timeout 10 some_command; [ $? -eq 124 ]
do
:
done
echo "Continuing on with script"
How it works
If a command times out, then, by default, timeout exits with code 124. We can use this to test whether the command timed out and, hence, whether it needs to be repeated. In more detail:
while timeout 10 some_command; [ $? -eq 124 ]; do
This starts a while loop by executing some_command with a 10 second timeout and then testing whether the exit code from timeout is 124 or not. If it is 124, then the while-loop repeats.
:
The command : is a no-op. We don't need any command within the body of the while loop, we use this command.
done
This marks the end of the while loop.
Special case: ill-behaved commands
This assumes that some_command responds responsibly to the TERM signal. If it doesn't, the exit code will be 128+9 instead of 124.
Documentation
From man timeout:
If the command times out, and --preserve-status is not set, then
exit with status 124. Otherwise, exit with the status of COMMAND.
If no signal is specified, send the TERM signal upon timeout. The
TERM signal kills any process that does not block or catch that
signal. It may be necessary to use the KILL (9) signal, since this
signal cannot be caught, in which case
the exit status is 128+9 rather than 124.

Related

stop currently running bash script lazily/gracefully

Say I have a bash script like this:
#!/bin/bash
exec-program zero
exec-program one
the script issued a run command to exec-program with the arg "zero", right? say, for instance, the first line is currently running. I know that Ctrl-C will halt the process and discontinue executing the remainder of the script.
Instead, is there a keypress that will allow the current-line to finish executing and then discontinue the script execution (not execute "exec-program one") (without modifying the script directly)? In this example it would continue running "exec-program zero" but after would return to the shell rather than immediately halting "exec-program zero"
TL;DR Something runtime similar to "Ctrl-C" but more lazy/graceful ??
In the man page, under SIGNALS section it reads:
If bash is waiting for a command to complete and receives a signal for which a trap has been set, the trap will not be executed until the command completes.
This is exactly what you're asking for. You need to set an exit trap for SIGINT, then run exec-program in a subshell where SIGINT is ignored; so that it'll inherit the SIG_IGN handler and Ctrl+C won't kill it. Below is an implementation of this concept.
#!/bin/bash -
trap exit INT
foo() (
trap '' INT
exec "$#"
)
foo sleep 5
echo alive
If you hit Ctrl+C while sleep 5 is running, bash will wait for it to complete and then exit; you will not see alive on the terminal.
exec is for avoiding another fork() btw.

Return value of kill?

CHILD=$!
sleep 2;
if kill -KILL ${CHILD} 2>/dev/null; then
echo "*** timed out after 2 seconds"
KILLED=yes
else
echo "terminated within time limit"
killed=no
fi
wait ${CHILD}
I'm a little confused on what is going on here and how the 'if' executes. My understanding is that this checks if killing a child process was successful then setting the KILLED variable to yes and printing out a message. Otherwise set KILLED to no and print a different message.
I thought that when a command is successful it returns a 0? If that's true wouldn't the 'if' interpret that as false and execute the else?
I'm also confused on what the messages printed out mean. I think I'm not understanding the difference between 'timed out' and 'terminated'. (i.e. I would assume the 'terminated' message would go where the 'timed out' message is, and vice versa).
Thanks!
It's a little counter-intuitive if you're coming from a language like C or Java, but in bash, a 0 exit status is actually interpreted as true. Here's an excerpt from the manual:
The most compact syntax of the if command is:
if TEST-COMMANDS; then CONSEQUENT-COMMANDS; fi
The TEST-COMMAND list is executed, and if its return status is zero, the CONSEQUENT-COMMANDS list is executed. The return status is the exit status of the last command executed, or zero if no condition tested true.
This is pretty useful, because there are usually a lot of ways a process can fail (giving different non-zero statuses), but only one way for everything to work correctly (zero status).
I think your other questions answer themselves after that :-)
kill returns an exit code of 0 (true) if the process still existed it and was killed. In this case, KILLED=yes.
kill returns an exit code of 1 (false) if the kill failed, probably because the process was no longer running. In this case, KILLED=no.

How do I stop a signal from killing my Bash script?

I want an infinite loop to keep on running, and only temporarily be interrupted by a kill signal. I've tried SIGINT, SIGUSR1, SIGUSR2. All of them seem to halt the loop. I even tried SIGINFO, but that wasn't supported by Linux.
#!/bin/bash
echo $$ > /tmp/pid # Save the pid
function do_something {
echo "I am doing stuff" #let's do this now, and go back to doing the thing that is to be done over and over again.
#exit
}
while :
do
echo "This should be done over and over again, but always wait for someething else to be done in between"
trap do_something SIGINT
while `true`
do
sleep 1 #so we're waiting for that other thing.
done
done
My code runs the function once, after getting a INT signal from another script, but then never again. It halts.
EDIT: Although I accidentally put en exit at the end of the function, here on Stack Overflow, I didn't in the actual code I used. Either way, it made no difference. The solution is SIGTERM as described by Tiago.
I believe you're looking for SIGTERM:
Example:
#! /bin/bash
trap -- '' SIGINT SIGTERM
while true; do
date +%F_%T
sleep 1
done
Running this example cTRL+C won't kill it nor kill <pid> you can however kill it with kill -9 <pid>.
If you don't want CTRL+Z to interrupt use: trap -- '' SIGINT SIGTERM SIGTSTP
trap the signal, then either react to it appropriately, in the function associate with the trap, or ignore it by for example associate : as command to get executed when the signal occurs.
to trap signals, bash knows the trap command
Reset trap to former action by executing trap with signal name only.
Therefore you want to (i think that's what you say you want with "only temporarily be interrupted by a kill signal"):
trap the signal at the begin of your script: trap signal custom_action
just before you want the signal to allow interrupting your script, execute: trap signal
At the end of that phase, trap again by: signal custom_action
to specify signals, you can also use their respective signal numbers. A list of signal names is printed with the command:
trap -l
the default signal sent by kill is SIGTERM (15), unless you specify a different signal after the kill command
don't exit in your do_something function. Simply let the function return to the section in your code where it was interrupted when the signal occured.
The mentioned ":" command has another potential use in your script, if you feel thusly inclined:
while :
do
sleep 1
done
can be an alternative to "while true" - no backticks needed for that, btw.
You just want to ignore the exit status.
If you want your script to keep running and not exit, without worrying about handling traps.
(my_command) || true
The parentheses execute that command in a subshell. The true is for compatibility with set -e, if you use it. It simply overrides the status to always report a success.
See the source.
I found this question to be helpful:
How to run a command before a Bash script exits?

running multiple binaries that does not terminate by itself in shell script

I have several binary in the same folder that I want to run in a sequence.
Each binary does not terminate by itself and is waiting for data from a socket interface. Also, I need to decide whether to run the next binary based on the output of the previous binary. I am thinking of running them in the background and redirect the output of the previous binary to a file and "grep" for the keyword. However, unless I use wait, I couldn't capture all the output I want from running the previous binary. But if I use wait, I can't get control back because the binary is listening on socket and wouldn't return.
What can I do here?
a sample code here:
/home/test_1 & > test_1_log
test_1_id=$!
wait
===> I also want to grep "Success" in test_1_log here.
===> can't get here because of wait.
/home/test_2 & >test_2_log
test_2_id=$!
wait
Thanks
Can you use sleep instead of wait?
The problem is that you can't wait for it to return, because it won't. At the same time, you have to wait for some output. If you know that "Success" or something will be output, then you can loop until that line appears with a sleep.
RC=1
while [ $RC != 0 ]
do
sleep 1
grep -q 'Success' test_1_log
RC=$?
done
that also allows you to stop waiting after, say, 10 iterations or something, making sure your script exits

How to restart a BASH script from itself with a signal?

For example I have script with an infinite loop printing something to stdout. I need to trap a signal (for example SIGHUP) so it will restart the script with different PID and the loop will start itself again from 0. Killing and starting doesn't work as expected:
function traphup(){
kill $0
exec $0
}
trap traphup HUP
Maybe I should place something in background or use nohup, but I am not familiar with this command.
In your function:
traphup(){
$0 "$#" &
exit 0
}
This starts a new process in the background with the original command name and arguments (vary arguments to suit your requirements) with a new process ID. The original shell then exits. Don't forget to sort out the PID file if your daemon uses one to identify itself - but the restart may do that anyway.
Note that using nohup would be the wrong direction; the first time you launched the daemon, it would respond to the HUP signal, but the one launched with nohup would ignore the signal, not restarting again - unless you explicitly overrode the 'ignore' status, which is a bad idea for various reasons.
Answering comment
I'm not quite sure what the trouble is.
When I run the following script, I only see one copy of the script in ps output, regardless of whether I start it as ./xx.sh or as ./xx.sh &.
#!/bin/bash
traphup()
{
$0 "$$" &
exit 0
}
trap traphup HUP
echo
sleep 1
i=1
while [ $i -lt 1000 ]
do
echo "${1:-<none>}: $$: $i"
sleep 1
: $(( i++ ))
done
The output contains lines such as:
<none>: 1155: 21
<none>: 1155: 22
<none>: 1155: 23
1155: 1649: 1
1155: 1649: 2
1155: 1649: 3
1155: 1649: 4
The ones with '<none>' are the original process; the second set are the child process (1649) reporting its parent (1155). This output made it easy to track which process to send HUP signals to. (The initial echo and sleep gets the command line prompt out of the way of the output.)
My suspicion is that what you are seeing depends on the content of your script - in my case, the body of the loop is simple. But if I had a pipeline or something in there, then I might see a second process with the same name. But I don't think that would change depending on whether the original script is run in foreground or background.

Resources