How to save multiple $! into variables and use it later in bash? - bash

I want to get the pids of two background processes,
sleep 20 & pid1=$\!; sleep 10 & pid2=$\!; echo "pid1: $pid1, pid2: $pid2"
and get output like below:
[1] 124646
[2] 124648
pid1: $!, pid2: $!
the output I desired to get is like:
pid1: 124646, pid2: 124648
Anyone know why and can help to achieve this?
[add 2018/01/02]
Sorry for really late response, one hand is busy, another is that I want to verify the script.
The actual script I want to run is like below:
sh -c "sleep 20 & pid1=$!; sleep 10 & pid2=$!; echo \"pid1: $pid1, pid2: $pid2\""
and as it will report bash: !: event not found, so I tried to add \ and become:
sh -c "sleep 20 & pid1=$\!; sleep 10 & pid2=$\!; echo \"pid1: $pid1, pid2: $pid2\""
and for make the problem simple, I just rmeove sh -c while this make it a quite different problem.
for my problem, I found out that below script will work:
sh -c 'sleep 20 & pid1=$!; sleep 10 & pid2=$!; echo "pid1: $pid1, pid2: $pid2"'
Yet there is another question, how to make below script to work:
name='bob'
# below script reports 'bash: !: event not found' error
sh -c "echo $name; sleep 20 & pid1=$!; sleep 10 & pid2=$!; echo \"pid1: $pid1, pid2: $pid2\""
# below script $name will not become bob
sh -c 'echo $name; sleep 20 & pid1=$!; sleep 10 & pid2=$!; echo \"pid1: $pid1, pid2: $pid2\"'

The backslash is causing the value to be the string $! verbatim. Don't put a backslash in the assignment.
On the command line, you may want to temporarily set +H to avoid getting event not found warnings; but this only affects the interactive shell. In a script, set -H is never active (and would be meaningless anyway).
(I'm speculating this is the reason you put the backslash there in the first place. If not, simply just take it out.)

Your syntax kindly incorrect, try this:
[root#XXX ~]# sleep 5 & pid1=$!; sleep 6 & pid2=$!; echo "pid1: ${pid1}, pid2: ${pid2}"
[1] 2308
[2] 2309
pid1: 2308, pid2: 2309

I have accomplished this in the past by using bash arrays to hold the PIDs. I had a sequence of database imports to run and when handled sequentially they took ~8 hours to complete. I launched them all as background processes and tracked the list of PIDs to watch for completion and it got the processing time down to 45 minutes.
Here is an example of launching background processes, storing the PIDs in an array, and then printing all of the array values:
$ pids=()
$ sleep 20 &
22991
$ pids+=($!)
$ sleep 20 &
23298
$ pids+=($!)
$ j=0;for i in "${pids[#]}";do ((j=j+1));echo 'pid'$j': '$i;done
pid1: 22991
pid2: 23298

Related

How to make $! to work inside `sh -c "$!"` [duplicate]

This question already has answers here:
How to save multiple $! into variables and use it later in bash?
(3 answers)
Closed 5 years ago.
Please help to see if there is someway to make below scrip workable?
name='bob'
# below script reports 'bash: !: event not found' error
sh -c "echo $name; sleep 20 & pid1=$!; sleep 10 & pid2=$!; echo \"pid1: $pid1, pid2: $pid2\""
# below script $name will not become bob
sh -c 'echo $name; sleep 20 & pid1=$!; sleep 10 & pid2=$!; echo \"pid1: $pid1, pid2: $pid2\"'
[Add 01/03]
This question is somewhat duplicate of this one, it's my fault that the description of the original question is not clear and accurate enough, and I try to create a new one to make it more specific and clear.
You seem to be trying to solve the event not found problem.
It happens when you have set -H in Bash and use an exclamation mark in an interactive command (outside of single quotes or a backslash escape).
I have posted an answer and I think half a dozen comments explaining what is happening and how to fix it.
set +H
This command disables the Bash history mechanism which uses the exclamation mark in interactive sessions.
In addition, within double quotes, you have to escape the dollar sign to pass it verbatim to the subshell.
sh -c "echo \"$name\"
sleep 20 & pid1=\$!
sleep 10 & pid2=\$!
echo \"pid1: \$pid1, pid2: \$pid2\""
The comments propose mixing quotes and disabling history expansion, but a better solution (IMO) is to pass the name as a parameter:
sh -c 'echo "$1"; sleep 20 & pid1=$!; ...' sh "$name"

Unable to run infinite loop process in background in terminal. [duplicate]

This question already has an answer here:
bg / fg inside a command line loop
(1 answer)
Closed 6 years ago.
I was trying out the commands as the video of Season1 Episode8 Processes and Jobs progressed. I have a bash terminal running on Ubuntu 16.04.
while true; do echo ping; sleep 1; done
^Z
Instead of getting:
[1]+ Stopped while true; do echo ping; sleep 1; done
I get:
[1]+ Stopped sleep 1
bg%1 further gives only
[1]+ sleep 1 &
instead of a series of ping in 1s interval in the background
Any ideas on why this happens and how to actually get a series of ping in 1s interval in the background would be appreciated.
Try:
bash <<< 'while true; do echo ping; sleep 1; done'
Result:
^Z
[1]+ Stopped bash <<< 'while true; do echo ping; sleep 1; done'
Or using a subshell:
(while true; do echo ping; sleep 1; done)
Result:
^Z
[1]+ Stopped ( while true; do
echo ping; sleep 1;
done )
Run your command with a & at the end, instead of stopping it. ^Z is too narrow to use with commands like this.
You run the command by either adding an & at the end which is easier you might find more trouble to end the process.
admin1#mysys:~$ while true; do echo ping; sleep 1; done&
[2] 14169
admin1#mysys:~$ ping
ping
ping
^C
admin1#mysys:~$ ping
ping
ping
^C
admin1#mysys:~$ ping
ping
ping
ping
^C
admin1#mysys:~$ ping
kill 14169
admin1#mysys:~$
As you can see, you will have to Cntrl + D or kill the process to stop it.
Another option would be to use 'screen'
Assuming you have screen installed, enter the terminal and execute the command 'screen'
Then you can execute the command:
while true; do echo ping; sleep 1; done
and then press Cntrl A and then D (keeping Cntrl pressed itself). This will detach you from screen and you can do whatever you want and the command will be executed in the background.
At any time you can list the current screen executing
screen -ls
and then connect to the screen back by executing
screen -r screen_name
This sounds a bit complicated but is a better way to handle things. You can find more details Here
Borrowing from [ this ] answer, Ctrl-Z generates the [ TSTP ] signal to your process and stopping the process is clearly not your intention.
To run a process in the background, do
process >/dev/null &
# Here the '>/dev/null' suppresses any output from the command
# popping up in the screen, this may or may not be desirable
# The & at the end tells bash that the command is to be run in backgroud
For example
$ ping -c 100 192.168.0.1 >/dev/null &
[1] 2849
Note the two numbers [1] & 2849 that bash gave you. The first one is the background process number. Say, if you wish to bring this process to the foregroud, you could use this number
fg 1 # Here fg stands for foreground
The second number is the process ID ie 2849. Say, you wish to terminate the
process, you could do it like below :
kill -9 2849 #-9 is for SIGKILL
Edit
In your case, you could wrap the loop inside a function like below
while_fun() {
while true
do
echo "PING"
done
}
and do
while_fun >dev/null &
Or do
while true
do
echo "PING"
done >/dev/null &
You could try something like this:
while true; do
echo ping 1
sleep 1
done;
Note that I have only placed semicolon ; on done - marking the end of statement. I tried this on my terminal and behaves as you expect.

Bash script to watch execution time of other scripts

I have a main script which run all the scripts in a folder.
#!/bin/bash
for each in /some_folder/*.sh
do
bash $each
done;
I want to know if execution of one of them lasts too long (more than N seconds). For example execution of script such as:
#!/bin/bash
ping -c 10000 google.com
will lasts very long, and I want my main script to e-mail me after N second.
All I can do now is to run all scripts with #timeout N option but it stops them!
Is it possible to E-mail me and not to stop execution of script?
Try this :
#!/bin/bash
# max seconds before mail alert
MAX_SECONDS=3600
# running the command in the background and get the pid
command_that_takes_a_long_time & _pid=$!
sleep $MAX_SECONDS
# if the pid is alive...
if kill &>/dev/null -0 $_pid; then
mail -s "script $0 takes more than $MAX_SECONDS" user#domain.tld < /dev/null
fi
We run the command in the background, then sleep for MAX_SECONDS in // and alert by email if the process takes more than what is permitted.
Finally, with your specific requirements :
#!/bin/bash
MAX_SECONDS=3600
alerter(){
bash "$1" & _pid=$!
sleep $MAX_SECONDS
if kill &>/dev/null -0 $_pid; then
mail -s "$2 takes more than $MAX_SECONDS" user#domain.tld < /dev/null
fi
}
for each in /some_folder/*.sh; do
alerter "$each" &
wait $_pid # remove this line if you wou'd like to run all scripts in //
done
You can do something like this:
( sleep 10 ; echo 'Takes a while' | sendmail myself#example.com ) &
email_pid=$!
bash $each
kill $email_pid
The first command is run in a subshell in the background. It first sleeps a while, then sends email. If the script $each finishes before the sleep expires, the subshell is killed without sending email.

jobs list and finding the process ID using Linux in Red Hat

I have wrote this script but when i run the job -ls and ps|grep i get no results here is my script:
#!/bin/bash
trap 'echo -e "kill Command given \n";exit 2'SIGINT SIGTERM
count=1
echo "start of the program"
while [ $count -le 10 ]
do
echo "Loop #${count}"
sleep 10
count=$[ count + 1 ]
done
echo "end of program"
This syntax is invalid:
trap 'echo -e "kill Command given \n";exit 2'SIGINT SIGTERM
because you don't have valid spacing. Use this instead:
trap 'echo -e "kill Command given \n"; exit 2' INT TERM
You're probably doing something else wrong, too, since this works fine for me:
# Start 10 sleep processes in the background.
for x in {1..10}; do
sleep 60 &
done
$ pgrep -c sleep
10
I'm getting the results I'm expecting, which is a count of the number of sleep processes currently running. If you're expecting something else, please update your question and provide some examples of your expected output.
I use this
ps | awk /$1/'{print $4; exit}'

run xterm -e without terminating

I want to run xterm -e file.sh without terminating.
In the file, I'm sending commands to the background and when the script is done, they are still not finished.
What I'm doing currently is:
(cd /myfolder; /xterm -ls -geometry 115x65 -sb -sl 1000)
and then after the window pops up
sh file.sh
exit
What I want to do is something like:
(cd /myfolder; /xterm -ls -geometry 115x65 -sb -sl 1000 -e sh file.sh)
without terminating and wait until the commands in the background finish.
Anyone know how to do that?
Use hold option:
xterm -hold -e file.sh
-hold Turn on the hold resource, i.e., xterm will not immediately destroy its window when the shell command completes. It will wait
until you use the window manager to destroy/kill the window, or if you
use the menu entries that send a signal, e.g., HUP or KILL.
I tried -hold, and it leaves xterm in an unresponsive state that requires closing through non-standard means (the window manager, a kill command). If you would rather have an open shell from which you can exit, try adding that shell to the end of your command:
xterm -e "cd /etc; bash"
I came across the answer on Super User.
Use the wait built-in in you shell script. It'll wait until all the background jobs are finished.
Working Example:
#!/bin/bash
# Script to show usage of wait
sleep 20 &
sleep 20 &
sleep 20 &
sleep 20 &
sleep 20 &
wait
The output
sgulati#maverick:~$ bash test.sh
[1] Done sleep 20
[2] Done sleep 20
[3] Done sleep 20
[4]- Done sleep 20
[5]+ Done sleep 20
sgulati#maverick:~$
Building on a previoius answer, if you specify $SHELL instead of bash, it will use the users preferred shell.
xterm -e "cd /etc; $SHELL"
With respect to creating the separate shell, you'll probably want to run it in the background so that you can continue to execute more commands in the current shell - independent of the separate one. In which case, just add the & operator:
xterm -e "cd /etc; bash" &
PID=$!
<"do stuff while xterm is still running">
wait $PID
The wait command at the end will prevent your primary shell from exiting until the xterm shell does. Without the wait, your xterm shell will still continue to run even after the primary shell exits.

Resources