73

I'm trying to get a simple while loop working in bash that uses two conditions, but after trying many different syntax from various forums, I can't stop throwing an error. Here is what I have:

while [ $stats -gt 300 ] -o [ $stats -eq 0 ]

I have also tried:

while [[ $stats -gt 300 ] || [ $stats -eq 0 ]]

... as well as several others constructs. I want this loop to continue while $stats is > 300 or if $stats = 0.

MERose
  • 4,048
  • 7
  • 53
  • 79
jake9115
  • 3,964
  • 12
  • 49
  • 78

3 Answers3

164

The correct options are (in increasing order of recommendation):

# Single POSIX test command with -o operator (not recommended anymore).
# Quotes strongly recommended to guard against empty or undefined variables.
while [ "$stats" -gt 300 -o "$stats" -eq 0 ]

# Two POSIX test commands joined in a list with ||.
# Quotes strongly recommended to guard against empty or undefined variables.
while [ "$stats" -gt 300 ] || [ "$stats" -eq 0 ]

# Two bash conditional expressions joined in a list with ||.
while [[ $stats -gt 300 ]] || [[ $stats -eq 0 ]]

# A single bash conditional expression with the || operator.
while [[ $stats -gt 300 || $stats -eq 0 ]]

# Two bash arithmetic expressions joined in a list with ||.
# $ optional, as a string can only be interpreted as a variable
while (( stats > 300 )) || (( stats == 0 ))

# And finally, a single bash arithmetic expression with the || operator.
# $ optional, as a string can only be interpreted as a variable
while (( stats > 300 || stats == 0 ))

Some notes:

  1. Quoting the parameter expansions inside [[ ... ]] and ((...)) is optional; if the variable is not set, -gt and -eq will assume a value of 0.

  2. Using $ is optional inside (( ... )), but using it can help avoid unintentional errors. If stats isn't set, then (( stats > 300 )) will assume stats == 0, but (( $stats > 300 )) will produce a syntax error.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Bravo. a classic way to answer a post. Well done. I'm assuming you can use the same syntax with `until` as well, yes? – SaxDaddy Aug 14 '14 at 21:05
  • 1
    @SaxDaddy More or less, as long as you take care to negate the conditions correctly: `while [ foo -o bar ]` becomes `until ! [ foo -o bar ]`, but `while foo || bar` becomes `until ! foo && ! bar`. – chepner Aug 14 '14 at 21:19
  • Seems this does not work when reading incoming data: `while [[ read -r line && $inputdata != "" ]]; do`. How do I achieve that? – uldics Dec 04 '20 at 06:20
  • When reading incoming data you can for example use this form for multiple conditions: `while read -r theline && [ "$theline" != "" ]` – SwanS Sep 21 '21 at 09:45
  • @chepner could you please help me with my condition: `while [[ $counter -le 97 -a "$breakCond" -le "$Mu"]]` – Arash Mar 01 '22 at 07:35
  • `-a` is not recognized inside `[[...]]`; use `&&`. – chepner Mar 01 '22 at 13:09
2

Try:

while [ $stats -gt 300 -o $stats -eq 0 ]

[ is a call to test. It is not just for grouping, like parentheses in other languages. Check man [ or man test for more information.

drewmm
  • 737
  • 6
  • 17
0

The extra [ ] on the outside of your second syntax are unnecessary, and possibly confusing. You may use them, but if you must you need to have whitespace between them.

Alternatively:

while [ $stats -gt 300 ] || [ $stats -eq 0 ]
Lighthart
  • 3,648
  • 6
  • 28
  • 47
  • 1
    Actually, `[[` is generally the preferred built-in to introduce a test expression. It has several advantages over the older single `[` syntax. – danfuzz Mar 20 '13 at 21:49
  • 4
    @danfuzz I know this is an old thread, but it would be great if you cite this in case people wanna read exactly why [[ is preferred over [. I know everyone can Google on their own... but still, it's good practice to cite.. makes your comment more robust and the reader's search faster and more efficient. – msimmer92 Nov 08 '18 at 15:31