shell: test with double bracket [[ does test on file, whereas single [ does not
Having this simple line:
user@Host:~$ ls -> fileA fileB
user@Host:~$ [[ -d $dir_name ]] && rm * # $dir_name does not exist, empty variable
user@Host:~$ ls -> fileA fileB #does not delete anything, as expected
user@Host:~$ [ -d $dir_name ] && rm *
user@Host:~$ ls -> #empty, although there is a test, all files are deleted, why?I know double bracket [[ is an extension of bash (or introduced by ksh), but why does not standard test turn off, when the obvious exit status is 0 in example above?
1 Answer
The correct usage would be [ -d "$dir_name" ].
Normally, unquoted variables are subject to word-splitting: if $dir_name contains spaces it will expand to multiple arguments in the resulting command's argv[] array; and if it's an empty string it will produce no args at all. The same rules apply to [, which is nothing more than a built-in command (and indeed a standalone executable as well).
So your second command is not the same as [ -d "" ] – it actually expands to [ -d ]. Due to different arg count, it becomes a completely different expression – the -d no longer indicates a "directory exists" test, but instead is itself used as a string parameter for the "string argument is non-empty" test (like [ foo ]), which of course succeeds.
However, these rules do not apply to [[, which receives special treatment from the shell's parser. Variable expansion inside [[ works differently – in many cases the argument is implicitly "double-quoted", so [[ -d $foo ]] and [[ -d "$foo" ]] are equivalent. That's where the difference comes from.
See also: