This seems like a pretty trivial thing to do, but I'm very stuck.
To execute something in the background, use &
:
>>> sleep 5 &
[1] 21763
>>> #hit enter
[1]+ Done sleep 5
But having a bashrc-sourced background script output job information is pretty frustrating, so you can do this to fix it:
>>> (sleep 5 &)
OK, so now I want to get the PID of sleep
for wait
or kill
. Unfortunately its running in a subshell so the typical $!
method doesn't work:
>>> echo $!
21763
>>> (sleep 5 &)
>>> echo $!
21763 #hasn't changed
So I thought, maybe I could get the subshell to print its PID in this way:
>>> sleep 5 & echo $!
[1] 21803 #annoying job-start message (stderr)
21803 #from the echo
But now when I throw that in the subshell no matter how I try to capture stdout of the subshell, it appears to block until sleep
has finished.
>>> pid=$(sleep 5 & echo $!)
How can I run something in the background, get its PID and stop it from printing job information and "Done
"?
Running shell command or script in background using nohup command. Another way you can run a command in the background is using the nohup command. The nohup command, short for no hang up, is a command that keeps a process running even after exiting the shell.
Normally, you can get your own PID with the variable $$. However, if a script runs one of its own functions as a background process - that doesn't work; all functions run in the background will get the PID of the parent script when $$ is used.
When summoning the process, redirect the shell's stderr to >/dev/null
for that summoning instance. We can do this by duplicating fd 2 so we could still use the duplicate fd for the process. We do all of these inside a block to make redirection temporary:
{ sleep 5 2>&3 & pid=$!; } 3>&2 2>/dev/null
Now to prevent the "Done" message from being shown later, we exclude the process from the job table and this is done with the disown
command:
{ sleep 5 2>&3 & disown; pid=$!; } 3>&2 2>/dev/null
It's not necessary if job control is not enabled. Job control can be disabled with set +m
or shopt -u -o monitor
.
We can also use command substitution to summon the process. The only problem we had is that the process still hooks itself to the pipe created by $()
that reads stdout but we can fix this by duplicating original stdout before it then using that file descriptor for the process:
{ pid=$( sleep 200s >&3 & echo $! ); } 3>&1
It may not be necessary if we redirect the process' output somewhere like /dev/null
:
pid=$( sleep 200s >/dev/null & echo $! )
Similarly with process substitution:
{ read pid < <(sleep 200s >&3 & echo $!); } 3>&1
Some may say that redirection is not necessary for process substitution
but the problem is that the process that may be accessing its stdout
would die quickly. For example:
$ function x { for A in {1..100}; do echo "$A"; sleep 1s; done }
$ read pid < <(x & echo $!)
$ kill -s 0 "$pid" &>/dev/null && echo "Process active." || echo "Process died."
Process died.
$ read pid < <(x > /dev/null & echo $!)
$ kill -s 0 "$pid" &>/dev/null && echo "Process active." || echo "Process died."
Process active.
exec 3>&1
so you can just have pid=$( sleep 200s >&3 & echo $! )
on the next lines.The set +m
disable monitor mode in bash. In other words it rid off the annnoying Done
message.
To enable again, use set -m
.
eg:
$ set +m
$ (sleep 5; echo some) &
[1] 23545 #still prints the job number
#after 5 secs
some
$ #no Done message...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With