bash
shouldn't print the job status when non-interactive.
If that's indeed for an interactive bash
, you can do:
{ pid=$(sleep 20 >&3 3>&- & echo "$!"); } 3>&1
We want sleep
's stdout to go to where it was before, not the pipe that feeds the $pid
variable. So we save the outer stdout in the file descriptor 3 (3>&1
) and restore it for sleep
inside the command substitution. So pid=$(...)
returns as soon as echo
terminates because there's nothing left with an open file descriptor to the pipe that feeds $pid
.
However note that because it's started from a subshell (here in a command substitution), that sleep
will not run in a separate process group. So it's not the same as running sleep 20 &
with regards to I/O to the terminal for instance.
Maybe better would be to use a shell that supports spawning disowned background jobs like zsh
where you can do:
sleep 20 &! pid=$!
With bash
, you can approximate it with:
{ sleep 20 2>&3 3>&- & } 3>&2 2> /dev/null; pid=$!; disown "$pid"
bash
outputs the [1] 21578
to stderr. So again, we save stderr before redirecting to /dev/null, and restore it for the sleep
command. That way, the [1] 21578
goes to /dev/null
but sleep
's stderr goes as usual.
If you're going to redirect everything to /dev/null anyway, you can simply do:
{ apt-get update & } > /dev/null 2>&1; pid=$!; disown "$pid"
To redirect only stdout:
{ apt-get-update 2>&3 3>&- & } 3>&2 > /dev/null 2>&1; pid=$!; disown "$pid"
sleep 2 & mypid=$!
?