4

I have a function, and inside that function is a while loop.

When I try to set a non local variable inside the while loop with an IF statement, then exit the entire function, suddenly the variable is no longer set?

function EXAMPLE {
  cat test.txt | while read LINE; do
    if [ "$LINE" = "FAIL" ]; then
      echo "Detected FAIL in file! Setting RETURN=fail and exiting function."
      RETURN="fail"
      return
    fi
  done
}

### START SCRIPT ###
EXAMPLE (Call example function)
echo "$RETURN"

For some reason, RETURN is empty. I have done this many many many times in the past though. Something about the while loop is preventing RETURN from being passed out of the function. Is "return" causing the script to break the loop and not the function?

Thanks

3
  • 2
    something | otherthing : in bash, otherthing runs in a subshell. When it ends, the subshell exits. Any variable creater or modified in the subshell is not available in the parent shell, which still have their former values. Commented Jan 3, 2014 at 18:17
  • So how do I send back RETURN from the while loop? Commented Jan 3, 2014 at 18:18
  • 2
    instead, use "return 1" when it finds "FAIL", and "return 0" if it ends the do..while without finding it. Then call it and use it's return value to decide what happened. Commented Jan 3, 2014 at 18:19

2 Answers 2

5

The easiest solution is to avoid the subshell in the first place, by using input redirection instead of a pipeline.

function EXAMPLE {
  while IFS= read -r line; do
    if [ "$line" = "FAIL" ]; then
      echo "Detected FAIL in file! Setting RETURN=fail and exiting function."
      RETURN="fail"
      return
    fi
  done < test.txt
}

In cases where the pipeline is unavoidable, bash 4.2 introduced the lastpipe option, which when enabled allows the last command in a pipeline to run in the current shell, not a subshell. This way, the value assigned to RETURN would be retained after the pipeline completes.

Even better, use the standard mechanism for signaling an error. Instead of setting the value of a custom parameter, just return a non-zero value:

function EXAMPLE {
  while IFS= read -r line; do
    if [ "$line" = "FAIL" ]; then
      echo "Detected FAIL in file! Exiting function with status 1."
      return 1
    fi
  done < test.txt
}
Sign up to request clarification or add additional context in comments.

3 Comments

OMG FINALLY THANKYOU!!!!!!!!!!!!!!!!!!!!!!! Been stuck on this for two days now! You are a life saver. The official solution is rather complex. This was just an example question, but your solution is perfect conceptually. THANK YOU!
I think the "while read LINE" should actually be "while read -r LINE"; "while" without the "-r" can give you strange behavior with uncontrolled input.
Yes, I wasn't as careful as I should have been with read in the past.
1

When you use a pipeline in bash, all the commands within the pipeline are executed in subshells. That means variables cannot survive when the subshell exits. In this cate it's easy to remove the pipe

function EXAMPLE {
  while read LINE; do
    if [ "$LINE" = "FAIL" ]; then
      echo "Detected FAIL in file! Setting RETURN=fail and exiting function."
      RETURN="fail"
      return
    fi
  done < text.txt
}

I'd recommend not using ALL CAPS so often: one day you'll use a variable named PATH and then have to figure out why commands stop working.

2 Comments

I'd do a "return 1" instead of a "return" [which returns 0, which usually means "ok", in many contexts]. That way you differentiate the return 1 when you find "FAIL" from the (induced) return 0 that will happen if the while...done ends without finding "FAIL"
Thank you, this was correct too, but someone beat you too it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.