79

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.

3 Answers 3

175

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.

Sign up to request clarification or add additional context in comments.

6 Comments

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 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.
Seems this does not work when reading incoming data: while [[ read -r line && $inputdata != "" ]]; do. How do I achieve that?
When reading incoming data you can for example use this form for multiple conditions: while read -r theline && [ "$theline" != "" ]
@chepner could you please help me with my condition: while [[ $counter -le 97 -a "$breakCond" -le "$Mu"]]
|
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.

2 Comments

I recommend [[ over [. See my comment on the other answer.
That's fair. I used [ ] because that was what the OP was trying to use. I've seen both used with success.
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 ]

2 Comments

Actually, [[ is generally the preferred built-in to introduce a test expression. It has several advantages over the older single [ syntax.
@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.

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.