Executing one shell command after a background command execution - bash

I have 2 commands which I am executing over shell command1 and command2. command1 takes long to complete (~2 minutes). So I can put it running in background using & but after that I want to execute command2 automatically. Can I do this on shell command line?

Try:
( command1; command2 ) &
Edit: Larger PoC:
demo.sh:
#!/bin/bash
echo Starting at $(date)
( echo Starting background process at $(date); sleep 5; echo Ending background process at $(date) ) &
echo Last command in script at $(date)
Running demo.sh:
$ ./demo.sh
Starting at Thu Mar 1 09:11:04 MST 2018
Starting background process at Thu Mar 1 09:11:04 MST 2018
Last command in script at Thu Mar 1 09:11:04 MST 2018
$ Ending background process at Thu Mar 1 09:11:09 MST 2018
Note that the script ended after "Last command in script", but the background process did its "Ending background process" echo 5 seconds later. All of the commands in the (...)& structure are run serially, but are all collectively forked to the background.

You can do so by putting both the commends in a shell script and do like below:-
command1 &
wait
command2 & #if you want to run second command also in background

Related

shell script not terminating child process after termination of parent process

I have a shell script (run.sh) that runs two commands (jmeter scripts) in parallel. If I terminate the shell script it is not killing the parallel process that got initiated, and they are running in background.
Can I make a shell script that would work in both Windows and Linux and will terminate all the process if Ctrl+C is pressed in the terminal that is executing run.sh?
#!/bin/sh
JmeterFolder=$1
$JmeterFolder/bin/jmeter.bat -n -t one.jmx -j oneLog.log &
$JmeterFolder/bin/jmeter.bat -n -t two.jmx -j twoLog.log &
wait
This is my code.
I have tried:
#!/bin/sh
trap 'stop' 2
stop()
{
kill -9 $pid1 $pid2
}
JmeterFolder=$1
$JmeterFolder/bin/jmeter.bat -n -t one.jmx -j oneLog.log &
pid1=$! &
$JmeterFolder/bin/jmeter.bat -n -t two.jmx -j twoLog.log &
pid2=$! &
wait
but this is not working when I execute it in Windows PowerShell, and don't think this is a right approach.
Wrote an infinite loop to simulate your jemter:
subtask.sh
#!/bin/bash
while true
do
date -R
sleep 1
done
Main script to start the substask and sleep 100 seconds to wait for Ctrl-C:
#!/bin/bash
trap 'kill $subpid; exit' SIGINT
./subtask.sh &
subpid=$!
sleep 100
result:
$ ./main.sh
Mon, 06 May 2019 02:03:32 -0700
Mon, 06 May 2019 02:03:33 -0700
^C$
When I press Ctrl-C "^C", it stopped after printing two lines.
If the main shell has been ended, the subtask's parent process ID will become 1, so it cannot pass signal to background process anymore..
I think the workaround can only be record the subtask PID in a file, and kill pid when you want to end them..

Pause bash script until signal of another one

What is the shortest way to sleep a bash script at a certain location until another script wakes it up to continue it's job?
Mayby using flock -u .. or blocking read on a pipe ?
Say scriptA sleeps and waits for being waken up by scriptB.
One way is, in A, before you sleep, write the pid to some file say scriptA.pid then falling in sleep.
When B is running, at the right moment, you can read the scriptA.pid file, to get the pid of A, then do pkill -P pidofA sleep thus, the sleep sub-process will be killed, and A will continue its execution.
I'm a fan of named pipes (fifo). scriptA.sh:
pipe='/tmp/mypipe'
mkfifo "$pipe"
echo "$0 going to sleep..."
# Should block
read < "$pipe"
echo "$0 continuing"
scriptB.sh
pipe='/tmp/mypipe'
mkfifo "$pipe"
echo "$0 waking other process"
# might block
echo > "$pipe"
echo "$0 exiting"
You will get a mkfifo: /tmp/mypipe: File exists from the second mkfifo, if that bothers you then test for existence first (-e "$pipe"). This does not tidy-up (rm) the fifo, not sure where that should go because timing of the application is critical to where you put that.
You could use the inter process signals: the kill command should be used to send a signal to a process using its pid.
The SIGSTOP signal stops the execution of the process.
The SIGCONT signal resumes the process execution.
The example script below:
stores the pid of the process in a file.
the script sends to its own process the SIGSTOP signal ($$ is the pid of the current bash process).
Hopefully, another process will resume the execution.
Give a try to this:
#!/bin/bash --
printf "%s" $$ > /tmp/aScript.pid
kill -STOP $$ # STOP the execution here
# execution continues here when the SIGCONT signal is received
printf "script %s: received the SIGCONT signal\n" $$
Test in a terminal:
$ ./aScript.sh &
[1] 26444
$ kill -CONT $(cat /tmp/aScript.pid)
script 26444: received the SIGCONT signal
1st method
The running script can stop itself -
$: cat flagfile
#!/usr/bin/bash
echo $$ > /tmp/flagfile.pid
kill -STOP $$
date
$: ./flagfile &
[1] 24679
$: ps -fu $LOGNAME | grep 'flagfile$'
P2759474 24679 24521 0 13:29 pts/0 00:00:00 /usr/bin/bash ./flagfile
[1]+ Stopped ./flagfile
Then any other script can restart it.
$: kill -CONT $(</tmp/flagfile.pid)
$: Wed Dec 12 13:36:01 CST 2018
That last line gave me back a prompt before the background process managed to output the date. :)
2nd method
If a delay is ok, you can have a trap break it out.
This isn't totally stopping the script, but you can set the delay and make it as freindly as you have leeway to wait for it to wake up.
$: cat flagfile
#!/usr/bin/bash
trap 'loop=0' USR1
loop=1
delay=2
echo $$ > /tmp/flagfile.pid
while (( loop )); do sleep $delay; done
date
$: ./flagfile &
[1] 25018
$: ps -fu $LOGNAME | grep 'flagfile$'
P2759474 25018 24521 0 13:42 pts/0 00:00:00 /usr/bin/bash ./flagfile
Wait as long as you like....
$: kill -USR1 $(</tmp/flagfile.pid)
$: Wed Dec 12 13:42:43 CST 2018
[1]+ Done ./flagfile

nohup bash call with sleep is evaluated incorrectly

I am trying do execute a script, detached, with a delay and in the background.
I have the following script:
#!/bin/sh
echo "[$(date)][${HOST}] Immediate date" > ~/out.log
nohup bash -c "sleep 2 && echo '[$(date)][${HOST}] Delayed date'" >> ~/out.log 2>&1 &
I expect that out.log would contain exactly two lines like so:
[Tue Apr 4 08:55:56 CEST 2017][] Immediate date
[Tue Apr 4 08:55:58 CEST 2017][] Delayed date
Note the timestamps being 2 seconds apart. But instead bot dates read exactly the same time...
[Tue Apr 4 08:55:56 CEST 2017][] Immediate date
[Tue Apr 4 08:55:56 CEST 2017][] Delayed date
When I try it without the nohup and bash -c the output is as expected, but the calling script would have to keep running, otherwise the second command will be aborted.
What causes the date not being different and to fix it?
edit:
I had simplified the command for stackoverflow. But now when I expand it again I run into another variable expansion problem. When I add the HOST variable:
HOST=foo.bar
nohup bash -c 'sleep 2 && echo "[$(date)][${HOST}]" Delayed date' >> ~/out.log 2>&1 &
the output file dos not contain foo.bar:
[Tue Apr 4 12:52:11 CEST 2017][]
The $(date) is evaluated by the outside shell script. If you swap the " with the ', it works as you expect it to work. This is because $(..) in double quotes is resolved, even if you put it in single quotes inside the double quoted string.
#!/bin/sh
echo "[$(date)][${HOST}] Immediate date" > ~/out.log
nohup bash -c 'sleep 2 && echo "[$(date)]['"${HOST}"'] Delayed date"' >> ~/out.log 2>&1 &
Edit: Make the code work w.r.t. $HOST + $HOST containing whitespace
I believe that double quotes in the nohup will evaluate the expression in the nohup command immediately.
Changing your double quotes into single quotes (and using the double quotes for the echo may help, e.g
#!/bin/sh
echo "[$(date)][${HOST}] Immediate date" > ~/out.log
nohup bash -c 'sleep 2 && echo "[$(date)][${HOST}] Delayed date"' >> ~/out.log 2>&1 &

Start background process from shellscript then bring back to foreground later

I'm trying to make a shell script that does the following:
Start program x
While x is running execute some commands, for example:
echo "blabla" >> ~/blabla.txt
After the execution of those commands program x should be running in the foreground, so that it can take user input.
So far I have:
~/x &
echo "blabla" >> ~/blabla.txt
However, I don't know how to move x back to the foreground. This is all called from a shell script so I don't know the job number of x to move to the foreground.
Note: everything has to be automated, no user interaction with the shell script should be needed.
Any suggestions are welcome :)
Although absolutely don't understand why someone may need such script, and I'm sure than exists more elegant and more better/correct solution - but ok - the next demostrating how:
The script what going to background (named as bgg)
#!/bin/bash
for i in $(seq 10)
do
echo "bg: $i"
sleep 1
done
read -p 'BGG enter something:' -r data
echo "$0 got: $data"
the main script (main.sh)
set -m #this is important
echo "Sending script bgg to background - will cycle 10 secs"
./bgg & 2>/dev/null
echo "Some commands"
date
read -r -p 'main.sh - enter something:' fgdata
echo "Main.sh got: ==$fgdata=="
jnum=$(jobs -l | grep " $! " | sed 's/\[\(.*\)\].*/\1/')
echo "Backgroung job number: $jnum"
echo "Now sleeping 3 sec"
sleep 3
echo "Bringing $jnum to foreground - wait until the BG job will read"
fg $jnum
run the ./main.sh - and the result will be something like
Sending bgg to background - will cycle 10 secs
Some commands
Mon Mar 3 00:04:57 CET 2014
main.sh - enter something:bg: 1
bg: 2
bg: 3
bg: 4
bg: 5
qqbg: 6
qqqqq
Main.sh got: ==qqqqqqq==
Backgroung job number: 1
Now sleeping 3 sec
bg: 7
bg: 8
bg: 9
Bringing 1 to foreground - wait until the BG job will read
./bgg
bg: 10
BGG enter something:wwwwwww
./bgg got: wwwwwww
You can use fg to bring the last background process to foreground

How do I limit the running time of a BASH script

I have a long running BASH script that I am running under CYGWIN on Windows.
I would like to limit the script to run for 30 seconds, and automatically terminate if it exceeds this limit. Ideally, I'd like to be able to do this to any command.
For example:
sh-3.2$ limittime -t 30 'myscript.sh'
or
sh-3.2$ limittime -t 30 'grep func *.c'
Under cygwin the ulimit command doesn't seem to work.
I am open to any ideas.
See the http://www.pixelbeat.org/scripts/timeout script the functionality of which has been integrated into newer coreutils:
#!/bin/sh
# Execute a command with a timeout
# License: LGPLv2
# Author:
# http://www.pixelbeat.org/
# Notes:
# Note there is a timeout command packaged with coreutils since v7.0
# If the timeout occurs the exit status is 124.
# There is an asynchronous (and buggy) equivalent of this
# script packaged with bash (under /usr/share/doc/ in my distro),
# which I only noticed after writing this.
# I noticed later again that there is a C equivalent of this packaged
# with satan by Wietse Venema, and copied to forensics by Dan Farmer.
# Changes:
# V1.0, Nov 3 2006, Initial release
# V1.1, Nov 20 2007, Brad Greenlee <brad#footle.org>
# Make more portable by using the 'CHLD'
# signal spec rather than 17.
# V1.3, Oct 29 2009, Ján Sáreník <jasan#x31.com>
# Even though this runs under dash,ksh etc.
# it doesn't actually timeout. So enforce bash for now.
# Also change exit on timeout from 128 to 124
# to match coreutils.
# V2.0, Oct 30 2009, Ján Sáreník <jasan#x31.com>
# Rewritten to cover compatibility with other
# Bourne shell implementations (pdksh, dash)
if [ "$#" -lt "2" ]; then
echo "Usage: `basename $0` timeout_in_seconds command" >&2
echo "Example: `basename $0` 2 sleep 3 || echo timeout" >&2
exit 1
fi
cleanup()
{
trap - ALRM #reset handler to default
kill -ALRM $a 2>/dev/null #stop timer subshell if running
kill $! 2>/dev/null && #kill last job
exit 124 #exit with 124 if it was running
}
watchit()
{
trap "cleanup" ALRM
sleep $1& wait
kill -ALRM $$
}
watchit $1& a=$! #start the timeout
shift #first param was timeout for sleep
trap "cleanup" ALRM INT #cleanup after timeout
"$#"& wait $!; RET=$? #start the job wait for it and save its return value
kill -ALRM $a #send ALRM signal to watchit
wait $a #wait for watchit to finish cleanup
exit $RET #return the value
The following script shows how to do this using background tasks. The first section kills a 60-second process after the 10-second limit. The second attempts to kill a process that's already exited. Keep in mind that, if you set your timeout really high, the process IDs may roll over and you'll kill the wrong process but this is more of a theoretical issue - the timeout would have to be very large and you would have to be starting a lot of processes.
#!/usr/bin/bash
sleep 60 &
pid=$!
sleep 10
kill -9 $pid
sleep 3 &
pid=$!
sleep 10
kill -9 $pid
Here's the output on my Cygwin box:
$ ./limit10
./limit10: line 9: 4492 Killed sleep 60
./limit10: line 11: kill: (4560) - No such process
If you want to only wait until the process has finished, you need to enter a loop and check. This is slightly less accurate since sleep 1 and the other commands will actually take more than one second (but not much more). Use this script to replace the second section above (the "echo $proc" and "date" commands are for debugging, I wouldn't expect to have them in the final solution).
#!/usr/bin/bash
date
sleep 3 &
pid=$!
((lim = 10))
while [[ $lim -gt 0 ]] ; do
sleep 1
proc=$(ps -ef | awk -v pid=$pid '$2==pid{print}{}')
echo $proc
((lim = lim - 1))
if [[ -z "$proc" ]] ; then
((lim = -9))
fi
done
date
if [[ $lim -gt -9 ]] ; then
kill -9 $pid
fi
date
It basically loops, checking if the process is still running every second. If not, it exits the loop with a special value to not try and kill the child. Otherwise it times out and does kill the child.
Here's the output for a sleep 3:
Mon Feb 9 11:10:37 WADT 2009
pax 4268 2476 con 11:10:37 /usr/bin/sleep
pax 4268 2476 con 11:10:37 /usr/bin/sleep
Mon Feb 9 11:10:41 WADT 2009
Mon Feb 9 11:10:41 WADT 2009
and a sleep 60:
Mon Feb 9 11:11:51 WADT 2009
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
Mon Feb 9 11:12:03 WADT 2009
Mon Feb 9 11:12:03 WADT 2009
./limit10: line 20: 4176 Killed sleep 60
Check out this link. The idea is just that you would run myscript.sh as a subprocess of your script and record its PID, then kill it if it runs too long.
timeout 30s YOUR_COMMAND COMMAND_ARGUMENTS
Below are all the options for "timeout" under coreutils:
$ timeout --help
Usage: timeout [OPTION] DURATION COMMAND [ARG]...
or: timeout [OPTION]
Start COMMAND, and kill it if still running after DURATION.
Mandatory arguments to long options are mandatory for short options too.
--preserve-status
exit with the same status as COMMAND, even when the
command times out
--foreground
when not running timeout directly from a shell prompt,
allow COMMAND to read from the TTY and get TTY signals;
in this mode, children of COMMAND will not be timed out
-k, --kill-after=DURATION
also send a KILL signal if COMMAND is still running
this long after the initial signal was sent
-s, --signal=SIGNAL
specify the signal to be sent on timeout;
SIGNAL may be a name like 'HUP' or a number;
see 'kill -l' for a list of signals
--help display this help and exit
--version output version information and exit
DURATION is a floating point number with an optional suffix:
's' for seconds (the default), 'm' for minutes, 'h' for hours or 'd' for days.
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.
GNU coreutils online help: <http://www.gnu.org/software/coreutils/>
Full documentation at: <http://www.gnu.org/software/coreutils/timeout>
or available locally via: info '(coreutils) timeout invocation'
You could run the command as a background job (i.e. with "&"), use the bash variable for "pid of last command run," sleep for the requisite amount of time, then run kill with that pid.

Resources