Reputation: 12166
Lets have standard file checking code in Bash:
f=$1
if [ ! -f "${f}" ];then
echo "Does not exit"
fi
Why, when I remove weak quoting like this: if [ ! -f ${f} ]
does this evaluate to true
but if [ ! -f "" ]
to false
?
Upvotes: 2
Views: 102
Reputation: 531708
There are several rules you must understand to make sense of POSIX shell and the test
command.
Unquoted parameter expansions are subject to word-splitting; quoted parameter expansions are not.
(Note: I say POSIX shell above because zsh
is a prominent example of a shell that does not subject unquoted parameter expansions to word-splitting by default. bash
does conform to the POSIX specification on this point.)
The behavior of the test
command is defined by the number of arguments it receives. Only after the argument count is determined can you start to interpret what the arguments mean.
test
can also be spelled [
, in which case the final argument must be ]
, but that argument is otherwise ignored for the purposes of determining how many arguments are passed to [
. That is, [ -f "$f" ]
and test -f "$f"
behave identically.
If the first argument of the test
command is !
, then it is ignored for the purposes of counting and evaluating the remaining arguments, but the result of the evaluation is negated.
Now, to your question. [ ! -f $f ]
is first subjected to parameter expansion. By point 1 above, $f
when unquoted expands to nothing; after word-splitting occurs, there is no non-empty string left to form a word. This means the shell sees [ ! -f ]
to be evaluated. This is recognized as the test
command receiving two arguments, !
and -f
. By point 4, this means that the one-argument expression -f
is evaluated, which is done by testing if the string is empty or not. -f
is not an empty string, so test
would succeed, but the !
inverts it, so test
ultimately fails.
If $f
were quoted, then parameter expansion produces [ ! -f "" ]
; there is an explicit empty string passed to test
. After setting aside !
and ]
, we are left with a two-argument call to test
. With two arguments, the first must be a recognized unary operator, and the second is treated as an argument to the first. Now, -f
is an operator that tests if its argument names an existing regular file. The empty string is not a valid file name, so there can be no such regular file; the test would fail, with !
inverting that so that the call to test
succeeds.
Upvotes: 4