1

Assuming a modern Bash version 5.x, is there any functional difference between the two expressions below:

[[ -z $a || -z $b ]]

vs

[[ -z $a ]] || [[ -z $b ]]

I'm not interested in debating the merits of [[ vs [ (POSIX, portability)

I would simply like to know whether there's a difference between [[ -z $a || -z $b ]] and [[ -z $a ]] || [[ -z $b ]] in either performance or function? This answer seems to indicate that they are equivalent, but I would like to know if I've understood it correctly.

luckman212
  • 473
  • 1
  • 7
  • 15
  • 1
    Functionally? Identical. Performance? No idea: you'd have to devise some benchmarks to test it, or dig into the bash source. I don't imagine there's a performance gain one way or the other. bash isn't a language built for speed in either case. – glenn jackman Dec 11 '21 at 14:17
  • Do you have any reason at all to believe there might be a difference? If you haven't hit a specific, practical problem, I'm not sure there's an on-topic question here. – Charles Duffy Dec 11 '21 at 14:41

1 Answers1

4

With only two boolean terms, both are equivalent.

[[ -z $a || -z $b ]] being a single statement it might be slightly more performant.

With a mixture of “and” and “or” operators, there is a difference: inside [[ … ]], && has precedence over || (like in C and many other languages), but outside [[ … ]], a chain of && and || is evaluated from left to right with equal precedence.

#!/usr/bin/env bash

printf %s\\n '[[ $a -eq 1 && $b -eq 1 || $c -eq 1 ]]'
for a in 0 1; do
  for b in 0 1; do
    for c in 0 1; do
      [[ $a -eq 1 && $b -eq 1 || $c -eq 1 ]]
      printf '%d AND %d OR %d = %d\n' $a $b $c $((!$?))
    done
  done
done

printf '\n%s\n' '[[ $a -eq 1 ]] && { [[ $b -eq 1 ]] || [[ $c -eq 1 ]];}'
for a in 0 1; do
  for b in 0 1; do
    for c in 0 1; do
      [[ $a -eq 1 ]] && { [[ $b -eq 1 ]] || [[ $c -eq 1 ]];}
      printf '%d AND (%d OR %d) = %d\n' $a $b $c $((!$?))
    done
  done
done

Output:

[[ $a -eq 1 && $b -eq 1 || $c -eq 1 ]]
0 AND 0 OR 0 = 0
0 AND 0 OR 1 = 1
0 AND 1 OR 0 = 0
0 AND 1 OR 1 = 1
1 AND 0 OR 0 = 0
1 AND 0 OR 1 = 1
1 AND 1 OR 0 = 1
1 AND 1 OR 1 = 1

[[ $a -eq 1 ]] && { [[ $b -eq 1 ]] || [[ $c -eq 1 ]];}
0 AND (0 OR 0) = 0
0 AND (0 OR 1) = 0
0 AND (1 OR 0) = 0
0 AND (1 OR 1) = 0
1 AND (0 OR 0) = 0
1 AND (0 OR 1) = 1
1 AND (1 OR 0) = 1
1 AND (1 OR 1) = 1
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
Léa Gris
  • 17,497
  • 4
  • 32
  • 41
  • 2
    You're comparing apples and oranges here. `[[ $a -eq 1 && $b -eq 1 || $c -eq 1 ]]` and `[[ $a -eq 1 ]] && [[ $b -eq 1 ]] || [[ $c -eq 1 ]]` are equivalent; and `[[ $a -eq 1 && ($b -eq 1 || $c -eq 1) ]]` and `[[ $a -eq 1 ]] && { [[ $b -eq 1 ]] || [[ $c -eq 1 ]];}` are equivalent – glenn jackman Dec 11 '21 at 18:48