2

I know [ -f file ], if we use it as a conditional check, checks if file is an ordinary file as opposed to a directory or special file; if yes, then the condition becomes true. Also I Know [ -e file ] Checks if file exists; is true even if file is a directory.

Let's say I have some code as below (saved as test.sh) for checking if its a regular file:

    #!/bin/bash 

    file=$1 
    if [ -e $file ] 
    then 
        echo "file exists" 
        if [ -f $file ] 
        then 
            echo "It's a regular file" 
        else 
            echo "It's not a regular file" 
        fi               
    else 
        echo "file doesn't exist" 
    fi

And I am running is as below:

    ./test.sh /etc/passwd

My output is as expected as below:

    file exists
    Its a regular file

And If I pass a invalid file or directory as argument:

    ./test.sh dsdsafds

Still the output is as expected:

    file doesn't exist

But if I am running the script without any argument, (meaning $1 will be null):

    ./test.sh

Both the if conditions are getting satisfied and my output is:

    file exists
    Its a regular file

Why is it so? Even though $1 is null, how can [ -e file ] and [ -f file ] be true?

And is there any way if I can output/print the Boolean values returned by such test operators?

agc
  • 7,973
  • 2
  • 29
  • 50
akhil pathirippilly
  • 920
  • 1
  • 7
  • 25

1 Answers1

4
  1. Because [ -f $file ] becomes [ -f ] when $file is null, so the expression is always true since -f is not a null string. From man bash:

    test and [ evaluate conditional expressions using a set of rules based on the number of arguments.

        0 arguments
            The expression is false.

        1 argument
            The expression is true if and only if the argument is not null.

        2 arguments
            If the first argument is !, the expression is true if and only if the second argument is null. If the first argument is one of the unary conditional operators listed above under CONDITIONAL EXPRESSIONS, the expression is true if the unary test is true. If the first argument is not a valid unary conditional operator, the expression is false.

    ...

    So, quoting the variable file prevents the bug you're facing (e.g [ -f "$file" ]).

  2. [ ... ] && echo true || echo false

oguz ismail
  • 1
  • 16
  • 47
  • 69
  • 1
    It worked and I understood the reason. Thanks for the quick response :) – akhil pathirippilly Sep 30 '18 at 04:14
  • 1
    This answer would be better if it referenced or explained how or why such an unintuitive syntax came about. Or perhaps that's another question... – agc Sep 30 '18 at 15:37
  • 1
    @oguzismail, Sorry the edit wasn't satisfactory. In the last revision was there some reason *not* to cite `info test` as the source of the quote? – agc Sep 30 '18 at 17:17
  • 1
    I'd suggest emphasizing that quoting prevents the bug, rather than telling people to quote but leaving its status as a preventative measure implicit. – Charles Duffy Sep 30 '18 at 20:04
  • 2
    @agc, this is a natural consequence of unquoted expansions going through string-splitting and globbing; nobody made an explicit design decision that `test` would have this syntax -- rather, it has the same expansion rules as *every other* regular command going back to the 1970s.. – Charles Duffy Sep 30 '18 at 20:06