cd ~<user> is possible but why can't we cd ~"$USER" or cd ~${USER}

That very much depends on the shell and the order the expansions are done in those shells.

~$user expands to the home directory of the user whose name is stored in $user in csh (where that ~user feature comes from), AT&T ksh, zsh, fish.

Note however these variations:

$ u=daemon/xxx csh -c 'echo ~$u'
/usr/sbin/xxx  # same in zsh/fish
$ u=daemon/xxx ksh93 -c 'echo ~$u'
~daemon/xxx

$ u=daemon/xxx csh -c 'echo ~"$u"'
Unknown user: daemon/xxx.
$ u=daemon/xxx zsh -c 'echo ~"$u"'
/usr/sbin/x  # same in fish

$ u=" daemon" csh -c 'echo ~$u'
/home/stephane daemon
$ u=" daemon" zsh -c 'echo ~$u'
~ daemon  # same in ksh/fish

$ u="/daemon"  csh -c 'echo ~$u'
/home/stephane/daemon  # same in zsh
$ u="/daemon"  fish -c 'echo ~$u'
~/daemon  # same in ksh

It expands to the home directory of the user named literally $user in bash (provided that user exists, which is very unlikely of course).

And to neither in pdksh, dash, yash, presumably because they don't consider $user to be a valid user name.


Tilde expansion is a separate step in the processing of the command line. It happens just before variable expansion.

If the tilde is followed by something other than a slash, it will expand to the home directory of the user whose name is following the tilde, as in, for example, ~otheruser. Since $USER is not expanded at that point and since it's unlikely to correspond to a valid username, the tilde is left unexpanded.

$USER is likely to be the username of the current user, so your expression could probably be replaced by just ~.


As other answers have pointed out the behavior depends on which order the shell does ~ and $ expansions and whether it will even do both for the same word.

The behavior you were looking for is possible to achieve in bash by a very small change to your command. Simply prefix the command with eval.

eval "cd ~$USER"

will change to the home directory of the user given by the username in the variable USER, provided $USER doesn't contain characters special to the shell (if there's a remote chance that it might, you should not pass it as argument to eval as that would be dangerous) or / characters and that there's an entry for that user in the system's user database.