Linux Bash - how to do the following with (),&& and & - linux

I want to execute a script but as soons as it started, I want it to echo the PID, so I'm doing the following:
./dummy.SCRIPT & echo $!
but I also want it to echo something when it is done executing the script so I'm guessing it should look something like:
( ./dummy.SCRIPT & echo $! ) && echo "Task Done!"
But it doesn't seem to work, the "Task Done!" echo executes at the same time the dummy.SCRIPT start's, what am I doing wrong!?

Rather than a command line execution, write a small script that does that for you.
$ cat monitordummy.sh
#!/bin/bash
./dummyscript &
pidofdummy=$!
while ps -p $pidofdummy > /dev/null
do
sleep 5 # Modify this according to your run time
echo -n "...." # This will be printed on the stdout
done
echo "Task Done for dummyscript"
Run the script:
$ chmod u+x monitordummy.sh
$ ./monitordummy.sh

It isn't pretty, but should do what you're describing:
{ ./dummy.SCRIPT & PID=$!; echo $PID; } && { wait $PID ; echo "Task Done!"; }
In the first block you run the script, save and print its PID. When that was OK, you run the second block in the background waiting for the child process to finish and print your message. Not that wait also propagates return code of the finished process, so you could at this point report done or failed accordingly.

Related

How do I redirect output to multiple files whose names are based on current time?

consider the following command:
while true; do echo 'Hit CTRL+C';sleep 1;done >> `date +"%H%M.txt"`
when I execute this command, it redirects output to a single file with the filename as the starting time of the command. How can I change this such that it saves to a different file every minute with the file name
date +"%H%M.txt"
at that given minute?
EDIT:
while true; do echo 'Hit CTRL+C';sleep 1;done
is just a substitute for a program that runs for a long time and outputs some data every second.i want to save the data that is output every minute into a separate file without having to start my program over again.
You need to re-evaluate the date on each instance of the loop.
while true; do echo 'Hit CTRL+C' > "`date +"%H%M"`.txt"; sleep 1; done
#!/bin/bash
while true ; do
echo 'Hit CTRL+C' # just for the screen
stock=$( date +"%H%M" )
while [[ $( date +"%H%M" ) == $stock ]] ; do
echo 'Hit CTRL+C' $stock
sleep 1
done > "$stock.txt" # no need '>>'
done
disk access every minute about
The output of your long time running command should redirect to a intermediate place (fifo), then use cat to pull log to different files from this fifo in every minute.
Here is a simple script wrapper, use SIGUSR1 to update log file name. Replace long-running-cmd with your command.
# in seconds
logger_update_duration=60
background_pid=
main_cmd=
function update_file_name() {
trap "update_file_name" USR1 # re-establish
file_name=$(date +"%Y-%m-%d-%H-%M-%S.log")
echo "update file name $file_name"
cat < ./logger > $file_name &
# after establish the new, check should kill the old
if [ x$background_pid != x ];then
echo "will kill the old $background_pid current is $$"
kill -TERM $background_pid
fi
background_pid=$!
}
trap "update_file_name" USR1
function clean() {
echo "perform clean..."
kill -TERM $!
kill -TERM $main_cmd
rm logger
}
trap "clean" EXIT
# create logger fifo
[ ! -p logger ] && mkfifo logger
# replace with your long time running operation
./long-running-cmd > ./logger &
main_cmd=$!
while true
do
kill -USR1 $$
sleep ${logger_update_duration}
done
The output of long-running-cmd should split into different log files in a period of one minute.

Bash wait for several simultaneous subprocesses and kill all on error

I'm running a bash script with multiple simultaneous commands (python scripts).
I'm trying to kill all the processes if one of them has failed.
The thing is that the python scripts are still running in the background, and if one of them has failed, my bash script doesn't know about.
Here's a snippet from my script:
set -a
trap cleanup_children SIGTERM
MY_PID=$$
function thread_listener () {
to_execute="$1"
echo "Executing $to_execute ..."
$to_execute &
PID=$!
trap 'echo killing $PID; kill $PID' SIGTERM
echo "Waiting for $PID ($to_execute) ..."
wait $PID || if `kill -0 $MY_PID &> /dev/null`; then kill $MY_PID; fi
}
function cleanup_children () {
for job in `jobs -p`
do
if `kill -0 $job &> /dev/null`; then
echo "Killing child number $job"
ps -p $job
kill $job
fi
done
}
function create_app1 () {
cd ${GIT_DIR}
python ./create-app.py -myapp
exit_code=$?
echo "Create app1 ISO result: ${exit_code}"
[ "${exit_code}" == "1" ] && exit 1
mv ${ISO_OUTPUT_DIR}/rhel-7.1.iso ${ISO_OUTPUT_DIR}/${ISO_NAME}.iso
}
function create_app2 () {
cd ${GIT_DIR}
python ./create-app.py -do-something
exit_code=$?
echo "Create app1 ISO result: ${exit_code}"
[ "${exit_code}" == "1" ] && exit 1
mv ${ISO_OUTPUT_DIR}/rhel-7.1.iso ${ISO_OUTPUT_DIR}/${ISO_NAME}.iso
}
export -f create_app1
export -f create_app2
echo "MY_PID=$MY_PID"
thread_listener create_app1 &
PID_APP1=$!
thread_listener create_app2 &
PID_APP2=$!
wait
kill $PID_APP1 2> /dev/null
kill $PID_APP2 2> /dev/null
Hm, this looks quite advanced ;). Do I assume correctly that you never see the "Create app1 ISO result" output then because the python script does not terminate? It might be an issue with the signal not being properly dispatched to bash background jobs. It might also be related to your python code not properly reacting to the signal. Have you checked out https://docs.python.org/2/library/signal.html? Sure you'd have to figure out the exact steps how to interrupt you python code while executing. I'd suggest to first make sure that the python code reacts to signals the way you want.

Get return value of command run in background with pipe and tee command

I want to get the return value of command run in background with pipe so i have below sample code.
#!/bin/bash
export RETVALUE="0"
CMD='ls ThisFileDoesNotExit'
LOG='tee -a log.txt'
$CMD | $LOG ; RETVALUE=${PIPESTATUS[0]} &
#$CMD | $LOG ; echo ${PIPESTATUS[0]} & // This print ret value 2
wait
echo "Return Value of ls is $RETVALUE"
Output:
Return Value of ls is 0 // It should print value 2
If i echo the return value from same command then it print correct return value.
But if store it in RETVALUE variable then it shows wrong value.
The problem is due to the & sign. This puts the RETVALUE assignment into the background, thus this command executes in a different environment than the current script, so the variables in your script do no get updated.
You also don't need to export the RETVALUE.
Also the wait command is not necessary, as bash does not process the next command until it has finished the previous one (unless you use the & to put it in the background)
#!/bin/bash
RETVALUE="0"
CMD='ls ThisFileDoesNotExit'
LOG='tee -a log.txt'
$CMD | $LOG
RETVALUE=${PIPESTATUS[0]}
echo "Return Value of ls is $RETVALUE"
EDIT: If you need to launch the process in the background, you will be forced to create a new script in order to recover the PIPESTATUS value, due to this variable is volatile. A possible solution is:
#!/bin/bash
CMD='ls ThisFileDoesNotExit'
LOG='tee -a log.txt'
TMPSCRIPT="file1.sh"
echo '#!/bin/bash' > $TMPSCRIPT
echo "$CMD |$LOG" >> $TMPSCRIPT
echo 'exit ${PIPESTATUS[0]}' >> $TMPSCRIPT
chmod +x $TMPSCRIPT
./$TMPSCRIPT &
MYPID=$!
wait $MYPID
RETVALUE=$?
rm $TMPSCRIPT
echo "Return Value of ls is $RETVALUE"

How to put an executable script on the background and wake it up after x minutes

I'm developing a timer in bash and this is what I've come up with so far:
#!/bin/bash
echo -n "For how long should I sleep? "
read answer
minutes=$((answer * 60))
sleep $answer && echo foo
It works like I was expecting except that I have to wait the X minutes I passed as input to get the prompt back.
Any ideas how I can execute this and have it execute it in the background so I can keep working with my terminal without waiting for script to finish?
Just add an & to the end of last line. That is:
sleep $answer && echo foo &
After you answer the prompt, suspend the script with Control-z. Then restart it in the background with bg.
You can simply put a sleep and echo in the background, like so:
$ { sleep 30; echo awake; } &
Or you can make a function too:
$ remind () { sleep $(($1 * 60)); shift; echo "$#"; }
$ remind 15 'check on dinner' &
[1] 28336
$ # more work...
# later...
$ check on dinner
[1]+ Done remind 15 'check on dinner'

Regarding PID Shell Script

I am calling another shell script testarg.sh within my main script.
the logfiles of testarg.sh are stored in $CUSTLOGS in the below format
testarg.DDMONYY.PID.log
example: testarg.09Jun10.21165.log
In the main script after the testarg process gets completed i need to grep the log file for the text "ERROR" and "COMPLETED SUCCESSFULLY".
How do i get the PID of the process and combine with DDMONYY for grepping. Also i need to check whether file
exists before grepping
$CUSTBIN/testarg.sh
$CUSTBIN/testarg.sh
rc=$?
if [ $rc -ne 0 ]; then
return $CODE_WARN
fi
You may background testarg.sh, which puts its pid into $!, and then wait for it:
#! /bin/bash
...
$CUSTBIN/testarg.sh &
LOGFILE=testarg.$(date +%d%b%y).$!.log # testarg.09Jun10.12345.log
wait $!
# ... $? is set as you expect ...
[ -f $LOGFILE ] && grep {pattern} $LOGFILE
...
If you can modify testarg.sh and it doesn't otherwise output anything, just change it to output its log file with a line like:
echo testarg.$(date +%blah).$$.log
then use:
fspec=$($CUSTBIN/testarg.sh)
in your parent.
Alternatively, you can provide a wrapper function to do the work:
#!/bin/bash
function fgpid() {
"$#" &
pid=$!
ps -ef | grep ${pid} | sed 's/^/DEBUG:/' >&2 # debugging
wait ${pid}
echo ${pid}
}
fspec=testarg.$(date +%d%b%y).$(fgpid sleep 5).log
echo ${fspec}
This produces:
pax> ./qq.sh
DEBUG:pax 2656 2992 con 15:27:00 /usr/bin/sleep
testarg.09Jun10.2656.log
as expected.
Or this if you think your executable may output something. This one stores the PID into a variable:
#!/bin/bash
function fgpid() {
"$#" &
pid=$!
ps -ef | grep ${pid} | sed 's/^/DEBUG:/' >&2 # debugging
wait ${pid}
}
fgpid sleep 5
fspec=testarg.$(date +%d%b%y).${pid}.log
echo ${fspec}
There are two simple ways to get the PID of some process you've just spawned.
One would be to modify the program being spawned (the subprocess) to have it write its PID to a file. You'd then read it therefrom with something like:
$CUSTBIN/testarg.sh
TSTARGSPID=$(cat /var/run/custbin.testarg.pid)
Another more elegant method would be:
$CUSTBIN/testarg.sh &
TSTARGSPID=$!
wait
# Do stuff with PID and output files

Resources