Why does mkdir fail (no such file or directory) in a script with BIN_DIR="~/bin/"?
Solution 1
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.
Solution 2
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.
Lloyd Perera
Updated on September 18, 2022Comments
-
Lloyd Perera over 1 year
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
-
terdon over 6 yearsThere'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. -
pa4080 over 6 years@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.
-
terdon over 6 yearsBut 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.
-
dessert over 6 yearsWhile that's totally true it's also right that using
$HOME
in scripts is a good idea. -
Eliah Kagan over 6 years@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. -
pa4080 over 6 years@EliahKagan, I've updated the answer with what I had in mind. Sorry for the late update but I had some work to do.
-
Eliah Kagan over 6 yearsThanks--your edit has somewhat improved this answer. But I'm not sure the reasoning you've given stands up to scrutiny.
~/bin
is as practical as$HOME/bin
. Really more--it's what most people already use interactively. You say quoting is different or is used differently in scripts, which seems obviously false to me, but maybe you could explain it in another edit? Most importantly, using$HOME
does not make attention unnecessary! Filenames often contain"
,$
, or`
. Whether$HOME
or~
is used, it's always necessary to consider if and how to quote the rest of the path. -
pa4080 over 6 years@EliahKagan, In an attempt to clarify what I mean, I've edited the answer one more time: )
-
pa4080 over 6 yearsAccording 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
. -
Eliah Kagan over 6 yearsThanks, but I don't know what you mean by "In the scripts things are back, for different reasons in most cases we want to quote the strings that we assign as variable's values." What does "things are back" mean? What are the "different reasons"?
-
Barmar over 6 years@pa4080 Expansion of
~
is not part of parameter expansion. -
pa4080 over 6 years@EliahKagan, I think I found what is the problem with this answer. Something more, I found that, I spent more effort to explain what I mean instead of just saying it. Thank you for the criticism and best regards!