2

I want to make a build chain script, and I don't want it to perform until the end if there are error during compilation.

It's the first time I write a more "elaborated" script in bash, and it just doesn't work:

  1. it doesn't echo ERROR although I have lines with the word error in it
  2. whatever the value of testError, the script just hangs in the line

this is the code:

testError=false

output=$(scons)
while read -r line; do
    if [[ $line == .*[eE]rror.* ]] ; then echo 'ERROR' ; $testError = true ; fi #$testError = true fi
done

echo $testError
if  $testError  ; then exit ; fi;

... other commands

EDIT: Following all posters answers and Bash setting a global variable inside a loop and retaining its value -- Or process substituion for dummies and How do I use regular expressions in bash scripts?, this is the final version of the code. It works:

testError=false

shopt -s lastpipe
scons | while read -r line; do
  if [[ $line =~ .*[eE]rror.* ]] ; then
    echo -e 'ERROR' 
    testError=true 
  fi 
  echo -e '.'
done

if  $testError  ; then
    set -e 
fi
Community
  • 1
  • 1
Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169

5 Answers5

2

You set the value of testError in a subshell induced by your pipeline. When that subshell exits (at the end of the pipeline), any changes you made disappear. Try this:

while read -r line; do
   if [[ $line == .*[eE]rror.* ]] ; then
     echo -e 'ERROR' 
     testError=true 
   fi #$testError = true fi
done < <( scons )

or, if you don't want or can't use process substitution, use a temporary file

scons > tmp
while read -r line; do
  if [[ $line == .*[eE]rror.* ]] ; then
    echo -e 'ERROR' 
    testError=true 
  fi #$testError = true fi
done < tmp

This eliminates the pipeline, so the changes to testError persist after the while loop.

And, if your version of bash is new enough (4.2 or later), there is an option that allows the while loop at the end of a pipeline to execute in the current shell, not a subshell.

shopt -s lastpipe
scons | while read -r line; do
  if [[ $line == .*[eE]rror.* ]] ; then
    echo -e 'ERROR' 
    testError=true 
  fi #$testError = true fi
done
chepner
  • 497,756
  • 71
  • 530
  • 681
1

You should try

set -e

this stops the script to continue if a command exit with a non zero status

or better

error_case() { # do something special; }
trap 'echo >&2 "an error occurs"; error_case' ERR

this run error_case function each time a command exit with a non zero status

See http://mywiki.wooledge.org/BashFAQ/105

Gilles Quénot
  • 173,512
  • 41
  • 224
  • 223
1

Are you trying to parse the output of scons?

This:

output=$(scons)
while read -r line; do
    if [[ $line == .*[eE]rror.* ]] ; then 
        echo 'ERROR' 
        testError=true
    fi
done

does not do that. Perhaps you want:

scons | while read -r line; do ... ; done
chepner
  • 497,756
  • 71
  • 530
  • 681
William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • The pipeline creates a subshell where changes to `testError` are no longer global. – chepner Feb 14 '13 at 00:02
  • @chepner I'm reading this question, related to what you're pointing: http://stackoverflow.com/questions/9012841/bash-setting-a-global-variable-inside-a-loop-and-retaining-its-value-or-proce – Stephane Rolland Feb 14 '13 at 00:13
  • @chepner testError does not need to be global; it only needs to be referenced within the loop. – William Pursell Feb 14 '13 at 00:32
  • No, he is explicitly testing the value *outside* the loop. – chepner Feb 14 '13 at 12:48
  • @chepner But he should not be! The code can be simplified substantially, and this answer is merely intended to point out his major error: namely that he was not parsing the output of scons. – William Pursell Feb 14 '13 at 13:32
1

Another bug is that you have spaces in the assignment. And skip the $

$testError = true

should be

testError=true

EDIT

testerror is changed in the subshell. Try

testerror=$(
    scons | while read -r line; do
        if [[ $line == .*[eE]rror.* ]] ; then
            echo true 
        fi #$testError = true fi
    done
)
user000001
  • 32,226
  • 12
  • 81
  • 108
0

I answer also because other answers didn't notice: the use of regular expression should be done this way, using =~and not ==:

if [[ $line =~ .*[eE]rror.* ]] ; then
...

cf How do I use regular expressions in bash scripts?

Community
  • 1
  • 1
Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169