This question is a sequel of sorts to my earlier question. The users on this site kindly helped me determine how to write a bash for
loop that iterates over string values. For example, suppose that a loop control variable fname
iterates over the strings "a.txt" "b.txt" "c.txt"
. I would like to echo
"yes!" when fname
has the value "a.txt"
or "c.txt"
, and echo
"no!" otherwise. I have tried the following bash shell script:
#!/bin/bash
for fname in "a.txt" "b.txt" "c.txt"
do
echo $fname
if [ "$fname" = "a.txt" ] | [ "$fname" = "c.txt" ]; then
echo "yes!"
else
echo "no!"
fi
done
I obtain the output:
a.txt
no!
b.txt
no!
c.txt
yes!
Why does the if
statement apparently yield true when fname
has the value "a.txt"
? Have I used |
incorrectly?
4 Answers 4
If you want to say OR
use double pipe (||
).
if [ "$fname" = "a.txt" ] || [ "$fname" = "c.txt" ]
(The original OP code using |
was simply piping the output of the left side to the right side, in the same way any ordinary pipe works.)
After many years of comments and misunderstanding, allow me to clarify.
To do OR
you use ||
.
Whether you use [
or [[
or test
or ((
all depends on what you need on a case by case basis. It's wrong to say that one of those is preferred in all cases. Sometimes [
is right and [[
is wrong. But that's not what the question was. OP asked why |
didn't work. The answer is because it should be ||
instead.
-
23Furthermore,
||
doesn't do a standard logic "OR" - it short-circuits, and the second command is run only if the first fails.holdenweb– holdenweb2016年12月12日 19:33:48 +00:00Commented Dec 12, 2016 at 19:33 -
45@holdenweb I'm pretty sure most modern optimized languages work the same way. No need to spend CPU cycles evaluating the second condition of
OR
if the first condition evaluates true.bahamat– bahamat2016年12月12日 20:50:18 +00:00Commented Dec 12, 2016 at 20:50 -
16I thought bash liked
==
but after seeing this answer, I decided to look it up. Apparently, "it can be used but isn't standard". I thought I'd put this here for others if your curious: stackoverflow.com/a/2237103harperville– harperville2018年09月17日 15:55:33 +00:00Commented Sep 17, 2018 at 15:55 -
11You can also use double bracket tests -
if [[ "$fname" = "a.txt" ]] || [[ "$fname" = "c.txt" ]]
(If you want or need to have the extra functionality associated with[[ ]]
).HankCa– HankCa2019年04月24日 05:45:19 +00:00Commented Apr 24, 2019 at 5:45 -
3@Massimo Since
[
is the same thing astest
, it's literally the same answer.bahamat– bahamat2020年06月03日 18:43:33 +00:00Commented Jun 3, 2020 at 18:43
The accepted answer is good but since you're using bash I'll add the bash solution:
if [[ "$fname" == "a.txt" || "$fname" == "c.txt" ]]; then
This is documented in the bash reference manual 3.2.4.2 Conditional Constructs
expression1 && expression2
True if both expression1 and expression2 are true.
expression1 || expression2
True if either expression1 or expression2 is true.
The && and || operators do not evaluate expression2 if the value of expression1 is sufficient to determine the return value of the entire conditional expression.
-
16+1 Agree this is the better answer (vs the "accepted" answer). Careful readers should note that the
||
(or&&
) are inside the conditional, which in this case necessarily uses[[...]]
and not[...]
(i.e., it's not the shell's "or"/"and"). I'll only add that[[ ... ]]
is not only more useful, supports more options (eg||
,&&
,<
,>
) but also "safer" than older[ ...]
; the old-a
and-o
from the single-bracket syntax is supported, too, and as expected, operators "and" take precedence over "or". tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETSmichael– michael2020年05月19日 23:30:43 +00:00Commented May 19, 2020 at 23:30 -
1I think @AnandShanbhag answer is better than this. Why spend neurons to bother about single or double brackets? C'mon, let do a favor to your readers: use testMassimo– Massimo2020年06月03日 19:49:41 +00:00Commented Jun 3, 2020 at 19:49
-
For the && operator is this true? "The && and || operators do not evaluate expression2 if the value of expression1 is sufficient to determine the return value of the entire conditional expression." I understand the || case, but does anyone know of an example with the && case where you wouldn't need to evaluate expression2? It doesn't seem logically possible, but I'm probably missing or misunderstanding something.jbobbins– jbobbins2020年09月25日 04:05:50 +00:00Commented Sep 25, 2020 at 4:05
-
2@jbobbins: If expression1 is false there is no need to evaluate expression2.jesse_b– jesse_b2020年09月25日 11:20:07 +00:00Commented Sep 25, 2020 at 11:20
-
4Wow, that's embarrassing... I guess my brain was locked into true mode. ;( Maybe it's time to retire :Djbobbins– jbobbins2020年09月25日 15:10:44 +00:00Commented Sep 25, 2020 at 15:10
You can also use OR condition like this
if test "$fname" = "a.txt" || test "$fname" = "c.txt"
-
7This is fundamentally the same as the accepted answer (and several comments) but more characters and (IMHO) less readable.G-Man Says 'Reinstate Monica'– G-Man Says 'Reinstate Monica'2020年05月15日 21:20:53 +00:00Commented May 15, 2020 at 21:20
-
It is easier to read test than brackets. Why make the code more cryptic than necessary?Massimo– Massimo2020年05月27日 15:12:26 +00:00Commented May 27, 2020 at 15:12
-
3That is your subjective opinion. Mine is that brackets are easier to read. None of our opinions is "correct", just saying.Martin Melka– Martin Melka2021年03月02日 09:22:06 +00:00Commented Mar 2, 2021 at 9:22
-
5Massimo, you’re replying under every answer stating that you think
test
is more convenient, yet nobody is upvoting these comments. How much cue do you need? You might think your way is best, but I think most people disagree. Sorry :)MS Berends– MS Berends2021年07月21日 11:56:34 +00:00Commented Jul 21, 2021 at 11:56 -
1@Massimo "more readable" is a subjective opinion. But
[]
and[[]]
are clearly more idiomatic.Barmar– Barmar2022年01月26日 19:23:16 +00:00Commented Jan 26, 2022 at 19:23
You can use or condition like this
if [ "$fname" = "a.txt" -o "$fname" = "c.txt" ]
-
41Yes you can, but using
-o
for "or" is marked as obsolete in the POSIX standard, as is using-a
for "and". New code should not use-o
or-a
.2020年01月04日 14:19:20 +00:00Commented Jan 4, 2020 at 14:19 -
2It also makes for unreliable code as the
[
command is not always able to parse its arguments correctly when given more than 4 arguments (beside[
and]
). Try for instance withfname='!'
with the[
builtin of thebash
shell.Stéphane Chazelas– Stéphane Chazelas2020年07月20日 12:24:46 +00:00Commented Jul 20, 2020 at 12:24
-o
within the same[ ]
.||
and separate[ ]
over-o
for portability simply because[
is not guaranteed to support more than 4 arguments. Of course if the target language isbash
, no one should be using[
anyways becausebash
's[[
is superior in many ways.if [[ "$fname" = "a.txt" ]] || [[ "$fname" = "c.txt" ]]
rather thanif [ "$fname" = "a.txt" ] || [ "$fname" = "c.txt" ]
?bash
, as you are already doing. One advantage of[[
is that it doesn't do word splitting (special case) so[[ $unquoted_var = string ]]
is safe.