Bash command substitution ( $(...) ) forcing process to foreground
Asked Answered
D

1

12

Summary: I have a bash script that runs a process in background, and is supposed to work as a normal command and inside a command substitution block such as $(...). The script itself spawns a process that forks to background. It can be reduced to this test case:

#!/bin/sh
echo something
sleep 5 &

Running this script in a shell will return immediately (and print "something"), running it inside $(...) will hang for 5 seconds, waiting for the backgrounded "sleep" to finish.

Applies to anything that is started inside the command substitution shell and spawns processes in background, including any children in that process tree apparently. Seems to affect both bash and zsh, haven't tried others.


Original question: I have a bash script that is supposed to print a value to stdout and also copy it to the X clipboard every time it runs.

#!/bin/sh
echo something
echo something | xclip -selection clipboard

This script (let's call it "something") is meant to be used to get this word (which is actually the output of another command) and be used in different ways such as:

$ something
something
$ xclip -o -selection clipboard
something
$ echo $(something)
^C

Prints to normal stdout, copies the output to the clipboard to be used in normal X applications, and should also be able to use the stdout with bash command substitution, to insert this word in the middle of any command.

However the bash command substitution seems to force xclip to stay alive in foreground. xclip normally daemonizes itself since the X clipboard requires that a client provides the clipboard contents, and the default behavior is to make it quit once the clipboard contents are replaced.

After having this issue with xclip I made the minimal test case that I wrote at the beginning of this question, so it seems to apply that anything that daemonizes inside the $(...) shell

Can anyone explain this behavior? Is there any way I can avoid it?

Dimarco answered 1/6, 2013 at 15:4 Comment(0)
B
18

If you want the backgrounded process to not interfere with command substitution, you have to disconnect its stdout. This will return immediately:

$ cat bg.sh 
#!/bin/sh
echo before
sleep 5 >/dev/null &
echo after
$ date; x=$(./bg.sh); date; echo "$x"
Sat Jun  1 13:02:26 EDT 2013
Sat Jun  1 13:02:26 EDT 2013
before
after

You will lose the ability to capture the backgrounded process's stdout, but if you're running it in the background you probably don't care. the bg.sh process can always write to disk.

Belfort answered 1/6, 2013 at 17:3 Comment(2)
Woo! Perfect and extremely simple solution. Thanks!Dimarco
By the way, redirecting to a file instead of /dev/null seems to work to capture the output, but at least here, the output file seems to get updated only after the background process exits. (I don't need it for this, though)Dimarco

© 2022 - 2024 — McMap. All rights reserved.