As everyone else said, the $dnam
variable needs double quotes. Here's why:
The [ ... ]
is an alias to the test
command. If you look in your system, you will see a file called /bin/[
or maybe /bin/usr/[
. On some systems, this is a hard link to /bin/test
or /bin/usr/test
. The if
statement executes what comes after the if
, and if that command returns a zero exit status, the if
statement will execute the then
clause. Otherwise, if there is an else
clause, that will execute instead.
To allow for boolean testing, Unix included the test
command, so you could do this:
if test -d "$directory"
then
echo "Directory $directory exists!"
fi
Later on, the /bin/[
was added as syntactic sugar. This is identical to the above:
if [ -d "$directory" ]
then
echo "Directory $directory exists!"
fi
Now, both [
and test
are builtin commands, but they are *still commands. This means that the shell interpolates the command and then executes it.
Try executing the following:
$ set -xv # Turns on shell debugging
$ dnam="/home/bryan/renametest/C D"
dnam="/home/bryan/renametest/C D"
+ dnam='/home/bryan/renametest/C D'
$ test -d $dnam
test -d $dnam
+ test -d /home/bryan/renametest/C D
$ echo $?
echo $?
+ echo 1
1
$ test -d "$dnam" # Now with quotes
test -d $dnam
+ test -d "/home/bryan/renametest/C D"
$ echo $?
echo $?
+ echo 0
0
$ set +xv # Turn off the debuggin
Each command is echoed twice. The first time as written, and the second time after the line is interpolated. As part of the interpolation, the shell splits parameters on white space. As you can see, the test
command is testing the presence of /home/bryan/renamtest/C
which doesn't exist and thus not writable. I'm actually surprised that the test
command didn't print an error message because you passed it an extra parameter.
In the second attempt, you added quotes. These quotes prevented the shell from splitting your parameters on the space and keep the directory name as a single parameter.
Since [ ... ]
is a command, you have to take into account the shell's interpolation of variables and other issues. And, if you're not absolutely careful, you can end up with errors.
Even worse, sometimes the [ ... ]
might work and sometimes it might not. If your directory name didn't contain spaces, it will work as expected. Imagine you're writing a program, and you test it and everything works because all directories you've tried don't have spaces. Then, someone uses your program, but has a space in the directory. A substantial number of shell script bugs are do to this type of issue in if
statements.
This is why Bash introduced the [[ ... ]]
tests. The [[
isn't a command but a statement. This means that the shell doesn't directly interpolate the results. Instead, the parameters are parsed, and then any interpolation is done. Thus, this would have worked:
dnam="/home/bryan/renametest/C D" # No "$" in front of the variable!
# Is the directory writable
if [[ ! -w $dnam ]] # No quotation marks needed!
then
# Not writable. Pop the error and exit.
echo "Directory $dnam is not writable"
err=1
fi
It's almost always better to use the [[ ... ]]
test rather than the [ ... ]
test, so go ahead and get into the habit.
One more minor error, you had:
$dnam="/home/bryan/renametest/C D"
This gets interpolated by the shell, so the variable being set is whatever the value of $dnam
just happens to be. If $dnam
happened to equal "foo", you would been doing this:
foo="/home/bryan/renametest/C D"
Not what you want.
You want to leave the $
off when you set variables:
dnam="/home/bryan/renametest/C D"