10

Why does the mkdir command fail with: "No such file or directory"?

#!/bin/bash

set -e

BIN_DIR="~/bin/"

if [ ! -d "$BIN_DIR" ]; then
  mkdir "$BIN_DIR"
fi
11

The error message is produced because the tilde ~ is quoted, as described in Zanna's answer. If you want to use the ~, the relevant part of the script should be:

BIN_DIR=~/bin/

If for whatever reason you want to quote the string, you can use the environment variable $HOME:

BIN_DIR="$HOME/bin/"

In my opinion the second approach is better practice.

|improve this answer|||||
  • 6
    There's nothing wrong with using ~ in scripts. it works in exactly the same way as in the command line. The problem is that quoting blocks tilde expansion as explained in Zanna's answer. – terdon Nov 17 '17 at 10:44
  • @terdon, I'm agree. But I haven't told there is something wrong, but it is a better idea, because you should pay less attention. – pa4080 Nov 17 '17 at 10:59
  • 5
    But there's absolutely no difference between the command line and a script here. The fact that this is in a script is completely irrelevant, you would have exactly the same error in the commandline. The issue is the quoting, not that it's in a script. – terdon Nov 17 '17 at 11:06
  • While that's totally true it's also right that using $HOME in scripts is a good idea. – dessert Nov 17 '17 at 11:42
  • 3
    @pa4080 Can you add an explanation of why you think it's better to expand $HOME than to use tilde expansion? The only explanation you've given is to say, "it is a better idea, because you should pay less attention." I have no idea what that means. Can you expound on it in an edit? Without it, there is nothing supporting your answer, so surely it belongs in it. Tilde expansion has been required by POSIX for quite a while now and the script's hashbang line is #!/bin/bash so I suppose portability isn't the reason. – Eliah Kagan Nov 17 '17 at 12:45
23

It doesn't work because ~ is quoted. Double quotes " suppress tilde expansion. There is no directory with the literal name ~/bin. As explained in man bash (emphasis mine):

Tilde Expansion

If a word begins with an unquoted tilde character (`~'), all of the characters preceding the first unquoted slash (or all characters, if there is no unquoted slash) are considered a tilde-prefix. If none of the characters in the tilde-prefix are quoted, the characters in the tilde-prefix following the tilde are treated as a possible login name. If this login name is the null string, the tilde is replaced with the value of the shell parameter HOME. If HOME is unset, the home directory of the user executing the shell is substituted instead. Otherwise, the tilde-prefix is replaced with the home directory associated with the specified login name.

You can remove the quotes, since ~ is the only character in the path ~/bin that will cause the shell to perform an expansion, and we want the expansion in this case. The shell won't perform any further expansions on the result of tilde expansion, at least in Bash 4, which all current or remotely recent releases of Ubuntu have. So even if your home directory contains unusual characters like spaces, it's okay.

Or you can use $HOME instead of ~, because parameter expansion is not suppressed by double quotes, only by single quotes. The double quotes do ensure that the expanded value is not itself subject to any further expansions, so word splitting or filename expansion will not occur. So $HOME works even with strangely named home directories, too, so long as you keep the double quotes.

|improve this answer|||||
  • According to this statement "parameter expansion is not suppressed by double quotes, only by single quotes": the output of cd '~' is -bash: cd: ~: No such file or directory. – pa4080 Nov 17 '17 at 16:24
  • 2
    @pa4080 Expansion of ~ is not part of parameter expansion. – Barmar Nov 17 '17 at 17:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.