Does the wait command work in a csh script to wait for more than 1 PID to finish?
Where the wait command waits for all the PID listed to complete before moving on to the next line
e.g.
wait $job1_pid $job2_pid $job3_pid
nextline
as the documentation online that I usually see only shows the wait command with only 1 PID, although I have read of using wait for multiple PID, like here :
http://www2.phys.canterbury.ac.nz/dept/docs/manuals/unix/DEC_4.0e_Docs/HTML/MAN/MAN1/0522____.HTM
which says quote "If one or more pid operands are specified that represent known process IDs,the wait utility waits until all of them have terminated"
No, the builtin wait command in csh can only wait for all jobs to finish. The command in the documentation that you're referencing is a separate executable that is probably located at /usr/bin/wait or similar. This executable cannot be used for what you want to use it for.
I recommend using bash and its more powerful wait builtin, which does allow you to wait for specific jobs or process ids.
From the tcsh man page, wait waits for all background jobs. tcsh is compatible with csh, which is what the university's documentation you linked is referring to.
wait The shell waits for all background jobs. If the shell is interactive, an interrupt will disrupt the wait and cause the shell
to print the names and job numbers of all outstanding jobs.
You can find this exact text on the csh documentation here.
The wait executable described in the documentation is actually a separate command that waits for a list of process ids.
However, the wait executable is not actually capable of waiting for the child processes of the running shell script and has no chance of doing the right thing in a shell script.
For instance, on OS X, /usr/bin/wait is this shell script.
#!/bin/sh
# $FreeBSD: src/usr.bin/alias/generic.sh,v 1.2 2005/10/24 22:32:19 cperciva Exp $
# This file is in the public domain.
builtin `echo ${0##*/} | tr \[:upper:] \[:lower:]` ${1+"$#"}
Anyway, I can't get the /usr/bin/wait executable to work reliably in a Csh script ... because the the background jobs are not child processes of the /usr/bin/wait process itself.
#!/bin/csh -f
setenv PIDDIR "`mktemp -d`"
sleep 4 &
ps ax | grep 'slee[p]' | awk '{ print $1 }' > $PIDDIR/job
/usr/bin/wait `cat $PIDDIR/job`
I would highly recommend writing this script in bash or similar where the builtin wait does allow you to wait for pids and capturing pids from background jobs is easier.
#!/bin/bash
sleep 4 &
pid_sleep_4="$!"
sleep 7 &
pid_sleep_7="$!"
wait "$pid_sleep_4"
echo "waited for sleep 4"
wait "$pid_sleep_7"
echo "waited for sleep 7"
If you don't want to rewrite the entire csh script you're working on, you can call out to bash from inside a csh script like so.
#!/bin/csh -f
bash <<'EOF'
sleep 4 &
pid_sleep_4="$!"
sleep 7 &
pid_sleep_7="$!"
wait "$pid_sleep_4"
echo "waited for sleep 4"
wait "$pid_sleep_7"
echo "waited for sleep 7"
'EOF'
Note that you must end that heredoc with 'EOF' including the single quotes.
Related
I need to launch a process within a shell script. (It is a special logging process.) It needs to live for most of the shell script, while some other processes will run, and then at the end we will kill it.
A problem that I am having is that I need to launch this process, and wait for it to "warm up", before proceeding to launch more processes.
I know that I can wait for a line of input from a pipe using read, and I know that I can spawn a child process using &. But when I use them together, it doesn't work like I expect.
As a mockup:
When I run this (sequential):
(sleep 1 && echo "foo") > read
my whole shell blocks for 1 second, and the echo of foo is consumed by read, as I expect.
I want to do something very similar, except that I run the "foo" job in parallel:
(sleep 1 && echo "foo" &) > read
But when I run this, my shell doesn't block at all, it returns instantly -- I don't know why the read doesn't wait for a line to be printed on the pipe?
Is there some easy way to combine "spawning of a job" (&) with capturing the stdout pipe within the original shell?
An example that is very close to what I actually need is, I need to rephrase this somehow,
(sleep 1 && echo "foo" && sleep 20 &) > read; echo "bar"
and I need for it to print "bar" after exactly one second, and not immediately, or 21 seconds later.
Here's an example using named pipes, pretty close to what I used in the end. Thanks to Luis for his comments suggesting named pipes.
#!/bin/sh
# Set up temporary fifo
FIFO=/tmp/test_fifo
rm -f "$FIFO"
mkfifo "$FIFO"
# Spawn a second job that writes to FIFO after some time
sleep 1 && echo "foo" && sleep 20 >$FIFO &
# Block the main job on getting a line from the FIFO
read line <$FIFO
# So that we can see when the main job exits
echo $line
Thanks also to commenter Emily E., the example that I posted that was misbehaving was indeed writing to a file called read instead of using the shell-builtin command read.
I'm looking for a bash command I can run in the background that will sleep for a bit (60 seconds), and the command will contain a specific text string I can grep out of a ps command.
I can't release a "dummy" script I'm afraid, so it needs to be a one line command.
I tried
echo "textneeded">/dev/null && sleep 60 &
But of course the only text I can grep for is the sleep, as the echo is over in a flash.
(The reasoning for this is it's for putting another script in "test" mode so it doesn't create child processes, but other functionality that ensures there are none of these processes running will still find something, and therefore wait. The other functionality isn't in a bash script.)
I had to do this to test a process killing script. You can use perl to set the process name.
perl -e '$0="textneeded"; sleep 60' &
Original props goes to this guy
I am running a shell script, something like sh script.sh in bash. The script contains many lines, some of which take seconds and others take days to execute. How can I kill the sh command but not kill its command currently running (the current line from the script)?
You haven't specified exactly what should happen when you 'kill' your script., but I'm assuming that you'd like the currently executing line to complete and then exit before doing any more work.
This is probably best achieved only by coding your script to behave in such a way as to receive such a kill command and respond in an appropriate way - I don't think that there is any magic to do this in linux.
for example:
You could trap a signal and then set a variable
Check for existence of a file (e.g touch /var/tmp/trigger)
Then after each line in your script, you'd need to check to see if each the trap had been called (or your trigger file created) - and then exit. If the trigger has not been set, then you continue on and do the next piece of work.
To the best of my knowledge, you can't trap a SIGKILL (-9) - if someone sends that to your process, then it will die.
HTH, Ace
The only way I can think of achieving this is for the parent process to trap the kill signal, set a flag, and then repeatedly check for this flag before executing another command in your script.
However the subprocesses need to also be immune to the kill signal. However bash seems to behave different to ksh in this manner and the below seems to work fine.
#!/bin/bash
QUIT=0
trap "QUIT=1;echo 'term'" TERM
function terminated {
if ((QUIT==1))
then
echo "Terminated"
exit
fi
}
function subprocess {
typeset -i N
while ((N++<3))
do
echo $N
sleep 1
done
}
while true
do
subprocess
terminated
sleep 3
done
I assume you have your script running for days and then you don't just want to kill it without knowing if one of its children finished.
Find the pid of your process, using ps.
Then
child=$(pgrep -P $pid)
while kill -s 0 $child
do
sleep 1
done
kill $pid
I would to give the user the feature to run the shell script in background.
My shell program instantiates a number of other shell scripts.
Here is a small code snippet of my script
./main.sh # Main script
in main.sh
I call preprocessing.sh
create_dir.sh
handle_file.sh
post_processing.sh
report_generation.sh
I would like to know if I have to initiate all the child script as well.. What is the syntax if i have to initiate all the scripts in background and at the end inform the user by displaying message in that test run is complete.
Thanks
Kiran
Start your processes in the background with & and then use bash's builtin wait command:
wait [n ...]
Wait for each specified process and return its termination sta‐
tus. Each n may be a process ID or a job specification; if a
job spec is given, all processes in that job’s pipeline are
waited for.
A couple of example are available here. For instance:
# wait on 2 processes
sleep 10 &
sleep 10 &
wait %1 %2 && echo "Completed!"
add "&" to the end of the commands
I call preprocessing.sh &
create_dir.sh &
...
I'm beginner at bash shell programming. Can you tell me about '$$' symbols in the bash shell?
If I try the following
#> echo $$
it prints
#>18756
Can you tell me what this symbol is used for and when?
It's the process id of the bash process itself.
You might use it to track your process over its life - use ps -p to see if it's still running, send it a signal using kill (to pause the process for example), change its priority with renice, and so on.
Process ids are often written to log files, especially when multiple instances of a script run at once, to help track performance or diagnose problems.
Here's the bash documentation outlining special parameters.
BASHPID, mentioned by ghostdog74, was added at version 4.0. Here's an example from Mendel Cooper's Advanced Bash-Scripting Guide that shows the difference between $$ and $BASHPID:
#!/bin/bash4
echo "\$\$ outside of subshell = $$" # 9602
echo "\$BASH_SUBSHELL outside of subshell = $BASH_SUBSHELL" # 0
echo "\$BASHPID outside of subshell = $BASHPID" # 9602
echo
( echo "\$\$ inside of subshell = $$" # 9602
echo "\$BASH_SUBSHELL inside of subshell = $BASH_SUBSHELL" # 1
echo "\$BASHPID inside of subshell = $BASHPID" ) # 9603
# Note that $$ returns PID of parent process.
if you have bash, a relatively close equivalent is the BASHPID variable. See man bash
BASHPID
Expands to the process id of the current bash process. This differs from $$ under certain circumstances, such as subshells
that do not require bash to be re-initialized.