Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute process in background, without printing "Done", and get PID

Tags:

bash

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"?

like image 957
jozxyqk Avatar asked Jul 19 '14 18:07

jozxyqk


People also ask

How do I run a process in the background shell?

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.

Can a process get its own PID?

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.


2 Answers

Solution A

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.

Solution B

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.
  • Optionally you can just create a permanent duplicate fd with exec 3>&1 so you can just have pid=$( sleep 200s >&3 & echo $! ) on the next lines.
like image 94
konsolebox Avatar answered Oct 19 '22 09:10

konsolebox


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...
like image 32
jm666 Avatar answered Oct 19 '22 10:10

jm666