0

I'm trying to comprehend what are all the possible constructs you can pass to if in bash.

In the GNU reference:

The syntax of the if statement is:

if test-commands; then
  consequent-commands;
[elif more-test-commands; then
  more-consequents;]
[else alternate-consequents;]
fi

The test-commands list is executed, and if its return status is zero, the consequent-commands list is executed...

So I understand that test-commands may be anything that returns a status. So first I thought that this must be one of the followings:

  • the test built-in (i.e. [ ] )
  • The [[ ]] syntax
  • some command (may be any executable)

And as I knew the test command, it has a ! operator (among others) that can inverse the return of the conditional statement you put in it, as such:

if [ ! -z "non-empty" ]; then echo "ok"; fi

Until then I was happy thinking I know bash. But then I found that I can do:

if ! [ -z "non-empty" ]; echo "Ok!"; fi

That breaked my heart - I thought that the ! thing belongs to the test command (also to the [[]] syntax) - but what is it outside of it? And why does if takes it?

What construct exactly is ! ? And are there other things acceptable?

YoavKlein
  • 2,005
  • 9
  • 38
  • 2
    Here is the manual section where the accepted answer's quote is found: [pipelines](https://www.gnu.org/software/bash/manual/html_node/Pipelines.html). The `[ ! ... ]` exclamation point (part of the syntax of the `test` command) is a different exclamation point from the `! [ ... ]` exclamation point (negation of a pipeline status), and neither are directly tied to `if`. – Amadan Oct 04 '21 at 09:56
  • Maybe see also https://stackoverflow.com/questions/36371221/checking-the-success-of-a-command-in-a-bash-if-statement – tripleee Oct 04 '21 at 10:00
  • 1
    `!` only has anything to do with `test`/`[` when you use it inside a `test` expression. The other times it's part of regular shell syntax and usable anywhere (not just in `if`). See the POSIX sh standard at https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_02 specifying the not-`test` version, and https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html specifying the `test` version. – Charles Duffy Oct 04 '21 at 11:43
  • BTW, `if [ ! -z "non-empty"; then echo "ok"; fi ]` (as shown in the question) is not at all legal syntax. – Charles Duffy Oct 04 '21 at 11:45
  • 2
    There are no specific _constructs_ which can be passed: After `if` must be a command (and this can be any command). If the command terminates with exit code 0, the `then` branch is taken; otherwise it's the `else` part. No fancy syntax here. – user1934428 Oct 04 '21 at 12:28
  • @CharlesDuffy you're right, that was a typo, fixed. – YoavKlein Oct 04 '21 at 13:27
  • 1
    `what are all the possible constructs you can pass to if in bash` you can put _anything_ inside `if`, you can write the whole script inside `if`, there are no limits. For example a loop and another expression inside an `if`: `if i=0; while ((i++ < 5)); do ((j=i*2)); done; ((j == 10)); then echo "j=$j"; fi;`. The `!` is like similar in construct like `if` or `while`, it just inverts the exit status of the command (actually whole pipeline). But `command !` - `!` is just a string, just like in `echo if` would print `if`. If the string `!` is passed to `[` command, then `[` reacts in specific way. – KamilCuk Oct 04 '21 at 13:31
  • 1
    `what is it outside of it?` is it's the first word in the list, it's a negation, if it is not the first word, it's just string `!` (but, also except history expansion). `why does if takes it?` any list of commands takes it `! echo 1`, even `! if true; then echo 2; fi` `What construct exactly is ! ?` It's part of pipeline as noted above and `And are there other things acceptable?` Yes, `!` `if` `while` `case` `until` are basically parsed the same way, see https://pubs.opengroup.org/onlinepubs/009604499/utilities/xcu_chap02.html#tag_02_04 – KamilCuk Oct 04 '21 at 13:34

0 Answers0