1

I am in the process of reading this bash guide from the Linux Documentation Project. On page 81 and 82 there's a short example script for testing whether an option is set:

if [ -o noclobber ]
    then
    echo "Your files are protected against accidental overwriting using redirection."
fi

I have run into some weird behavior when trying to negate the test. I am getting a return value of 0 for all options that are turned on for [ -o OPTION ] and [ ! -o OPTION ]. Here's an example:

$ set -o | grep errex
errexit         off
$ [ -o errexit ]; echo $?
1
$ [ ! -o errexit ]; echo $?
0
$ set -o | grep history
history         on
$ [ -o history ]; echo $?
0
$ [ ! -o history ]; echo $?
0
timgeb
  • 76,762
  • 20
  • 123
  • 145
  • I don't see that -o does what you expect `man test | grep -A1 "\-o"`. – Vytenis Bivainis Oct 04 '14 at 21:52
  • 1
    You do `set -o | grep errex`: note that that's not a reliable way to check an option. Pipes are executed in subshells, so `set -o` is executed in a subshell, and they don't necessarily inherit all of the parents shells options. E.g. (`zsh`): `setopt monitor; set -o | grep monitor` -> `monitor off`: _clearly wrong_, because `set -o` is executed in a subshell of where the option is set. (On the other hand: `(setopt monitor; set -o) | grep monitor` -> `monitor on`.) – Carl Sep 20 '22 at 14:46

1 Answers1

4

Use [[ ! -o option ]] instead. Parsing of expressions in [[ ]] is more predictable.

The result you're seeing with [ is because there are two -o operators in bash's test builtin: unary -o option to check if an option is set, and binary test1 -o test2 to check if either test is true (logical or).

You are passing test three arguments, !, -o and history. Let's see what POSIX says about it how to parse three arguments:

3 arguments:
  - If $2 is a binary primary, perform the binary test of $1 and $3.
  - If $1 is '!', negate the two-argument test of $2 and $3.
  (...)

-o, is indeed a binary operator, so it performs the test of $1 and $3, which become the "is non-empty" check (like in [ ! ] and [ history ]). The result is therefore true.

The second interpretation is what you expected, but it's not used since the first interpretation matched.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • +1. `! [ -o option]` is also possible in the unlikely event that you have a shell which doesn't understand `[[`. – rici Oct 05 '14 at 05:49
  • @rici `-o` for options is in itself bash specific, so that would be unlikely indeed. – that other guy Oct 05 '14 at 06:52
  • Good point. It's still useful to know that ! is not just for inside brackets. – rici Oct 05 '14 at 06:57
  • Thanks, can you explain why the behavior when using [[ is working like expected? – timgeb Oct 05 '14 at 19:35
  • 1
    @timgeb It's basically a bug or misfeature in `[` that has to remain in place for backwards compatibility. `[[` came decades later and didn't have to carry the same legacy baggage. – that other guy Oct 05 '14 at 20:40
  • @thatotherguy alright, so [[ works as expected because it does not try the "first interpretation" for three arguments you provided? – timgeb Oct 05 '14 at 21:05
  • @timgeb `[[` doesn't use binary `-o` at all, avoiding the problem. But it also uses a proper expression parser with well defined operator precedence, rather than taking shortcuts based on argument count. – that other guy Oct 06 '14 at 02:53