I want to know if there's a way to put the ampersand in a variable and still use it to send a process to the background.

This works:

if [ "$BCKGRND" = "yes" ]; then
    sleep 5 &
    sleep 5

But wouldn't it be cool to accomplish those five lines with only one? Like so:

sleep 5 ${BCKGRND}

But that doesn't work. If BCKGRND isn't set it works - but when it is set it's interpreted it as a literal '&' and errors out.

  • after using the trailing ampersand, echo $! returns the PID
    – noobninja
    Jan 16 '20 at 4:08

You can flip things and variabilise “foregrounding”:

sleep 5 & ${FOREGROUND}

Set FOREGROUND to true or empty to run the process in the background. (Setting FOREGROUND to true to run in the background is admittedly confusing! Appropriate variable names are left as an exercise for the reader.)

  • 4
    that's nice but it won't work in a shell without job control (ie any script unless set -m was used).
    – mosvy
    Jan 9 '19 at 15:47

It's not possible to use a variable to background the call because variable expansion happens after the command-line is parsed for control operators (such as && and &).

Yet another option would be to wrap the calls in a function:

mayberunbg() {
  if [ "$BCKGRND" = "yes" ]; then
    "$@" &

... and then set the variable as needed:

$ BCKGRND=yes mayberunbg sleep 3
[1] 14137
[1]+  Done                    "$@"
# or
$ mayberunbg sleep 3
[1] 14203
[1]+  Done                    "$@"
$ BCKGRND=no mayberunbg sleep 3
# 3 seconds later
  • What, no ed? +1 anyway, this is the cleanest solution. Jan 9 '19 at 14:46
  • LOL @StephenKitt; now the gears are turning
    – Jeff Schaller
    Jan 9 '19 at 14:47
  • I liked the eval answer for its simplicity but my actual real-world command I wanted to background was far too complicated with too many variables in it to be comfortable using eval. @jeff-schaller gave the answer that pointed me in the direction I went. Instead of a function, though, I put the entire command in a variable then used his if statement style to execute the command with or without the &. Jan 9 '19 at 17:31

You would probably have to use eval:

eval "sleep 5" "$BCKGRND"

eval causes the shell to re-evaluate the arguments given. A literal & would therefore be interpreted as & at the end of a command and not as an argument to the command, putting the command in the background.

  • 2
    A answer containing eval should contain a warning, that this should be handles with care. See e.g. this answer.
    – Ralf
    Jan 9 '19 at 14:30
  • I don't get what the problem is with "$BCKGRND" evaluating to an empty argument.
    – mosvy
    Jan 9 '19 at 14:48
  • 2
    @Ralf absolutely irrelevant in this case. There's nothing special about eval -- you can execute commands via arithmetic expansions, for instance. Maybe there should be such a warning against using bash (or any similar shell) at all ;-)
    – mosvy
    Jan 9 '19 at 14:54
  • 1
    @Kusalananda eval will join its arguments with spaces before doing the actual eval. Just try it: eval printf "'{%s}\n'" foo "" "" "". eval foo "" "" "" "" is completely similar to eval foo, no matter what IFS or other thing is.
    – mosvy
    Jan 9 '19 at 15:22
  • 1
    The command being eval'ed should be in double quotes if it contains any special characters, e.g. eval 'sleep $TIMEOUT' "$BACKGROUND". Otherwise you could get double expansions if the variable expands to another variable or contains special characters. Also, nested quoting can get tricky.
    – Barmar
    Jan 10 '19 at 1:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.