1

Why ~ does not expand in $ ./foo.sh?

MYPATH_TO=~"/Documents 2/temp"
echo ~
echo "$MYPATH_TO"
mkdir "$MYPATH_TO"

when run in /home/mint/Documents 2 creates /home/mint/Documents 2/~/Documents 2/temp (echo outputs /home/mint ~/Documents 2/temp). I have read Why doesn't the tilde (~) expand inside double quotes? and put ~ outside of quotes, still does not expand, why?

ADDED: it does not expand because it is quoted in mkdir, but then how to script if I expect both spaces and symbols like ~ in path?

ADDED 2: when I tried mkdir w/out quotes for path w/out spaces:

MYPATH_TO=~"/Documents/temp"
mkdir $MYPATH_TO

I got error mkdir: cannot create directory ‘~/Documents/temp’: No such file or directory. Why? I have Documents in my home. I'm lost...

System: Linux Mint 19.2

  • 2
    Use $HOME instead of ~; then you don't have to worry about expansion. – ajgringo619 Oct 8 '19 at 1:09
  • The right side of = is considered "quoted". There is a high risk that it doesn't get expanded. One solution is to replace "~" with '$HOME' with ${MYPATH_TO/#~/'$HOME'} (in bash) or similar. – Isaac Oct 8 '19 at 1:44
  • 2
    You also need to "unquote" the slash: MYPATH_TO=~/"Documents/temp" – Freddy Oct 8 '19 at 1:48
2

A quick fix is to put the first slash outside the quoted part:

MYPATH_TO=~/"Documents 2/temp"
echo "$MYPATH_TO"

/home/user/Documents 2/temp

Another example:

bash -c 'a=~"/." b=~\/. c=~/. d=~/"."; echo "$a $b $c $d"'
~/. ~/. /home/user/. /home/user/.

Only $c and $d will have the tilde expanded in bash, dash, mksh and yash. ksh93 will also expand it in $a, and zsh in $a and $b.

According to the POSIX standard (emphasis mine):

A "tilde-prefix" consists of an unquoted <tilde> character at the beginning of a word, followed by all of the characters preceding the first unquoted <slash> in the word. In an assignment [...] multiple tilde-prefixes can be used: at the beginning of the word (that is, following the equal sign of the assignment), following any unquoted colon, or both. A tilde-prefix in an assignment is terminated by the first unquoted colon or slash.

This is just a pointless annoyance IMHO, because despite non-conforming to its letter, it's only ksh93 which implements what seems to be the intention, namely allowing user names containing slashes (you can escape them with backquotes in ksh93):

cat <<'EOT' >getpwnam.c
#include <pwd.h>
static struct passwd pwd = { "", "", 666, 666, "", "/hell", "" };
struct passwd *getpwnam(const char *name){ return &pwd; }
EOT
cc -Wall -shared getpwnam.c -o getpwnam.so
for sh in dash bash yash mksh zsh ksh93; do
    printf '%s\t' "$sh"
    LD_PRELOAD=./getpwnam.so HOME=nowhere \
      "$sh" -c 'echo ~q ~\/q ~"/q"'
done

dash    /hell ~/q ~/q
bash    /hell ~/q ~/q
yash    /hell ~/q ~/q
mksh    /hell ~/q ~/q
zsh     /hell nowhere/q nowhere/q
ksh93   /hell /hell nowhere/q

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.