5

I wrote a script that contains an until loop. This loop should run till a boolean variable is set to true from outside the loop. Unfortunately the loop ignores that the variable has been set to true and keeps running. Here are a few lines which generate this problem:

boolean=false
{ sleep 5 && boolean=true && echo "boolean is true now" ; } &
{ until [ "$boolean" = true ] ; do sleep 1 && echo $boolean ; done ; } &&
echo "boolean is true now: $boolean"

The output this generates is:

false
false
false
false
boolean is true now
false
false
false
...

How can I make the loop exit when the boolean is set to true?

3 Answers3

13

It's possible to use signals to communicate between the foreground and background shells:

#!/bin/bash

# global variable for foreground shell
boolean=false

# register a signal handler for SIGUSR1
trap handler USR1

# the handler sets the global variable
handler() { boolean=true; }

echo "before: $boolean"

# Here, "$$" is the pid of the foreground shell
{ sleep 5; kill -USR1 $$; echo "finished background process"; } &

# busy waiting
until $boolean; do 
    echo "waiting..."
    sleep 1
done

echo "after: $boolean"

output

before: false
waiting...
waiting...
waiting...
waiting...
waiting...
finished background process
after: true
glenn jackman
  • 85,964
8

The & character makes a background process. A background process is executed asynchronously in a subshell. Variables can be passed from a parent shell to sub shell, not the other way around. However, you can do a workaround if you are really in need of the value set in the child shell:

boolean=$(mktemp) && echo "false" >$boolean
{ sleep 5 && echo true >$boolean && echo "boolean is true now" ; } &
{ until [ "$(cat $boolean)" = "true" ] ; do sleep 1 && cat $boolean ; done ; } &&
echo "boolean is true now: $(cat $boolean)"

That makes a temporary file whose content is the boolean value. In the until loop that file is checked until it contains true.

Notice:

I recommend to do the following in your script (if possible):

{ sleep 5 && echo "background process done" ; } &
wait
echo "continue in foregound."

wait waits for the background process to finish.

jlliagre
  • 61,204
chaos
  • 48,171
4

You mistakenly assume that the boolean you set to true in the second line is the same boolean that you test for in the until statement. That is not the case, you start new process, with a new shell in the background and boolean (the one you test for), never gets assigned.

Anthon
  • 79,293