1

I'm trying to understand Bash variable interpolation.

I want to use readlink to show the path a symlink points to.

If I use a string it works.

$ echo "$(readlink -- ~/.gitconfig)"
/Users/jord/.dotfiles/gitconfig

For some reason it doesn't work when I try to use a variable instead of a string.

$ file="~/.gitconfig"
$ echo "$(readlink -- $file)"

Nothing is printed except a blank line.

If I do the same thing, but with dirname (as an example) instead, the variable interpolation works as I would expect.

$ file="~/.gitconfig"
$ echo "$(dirname -- "$file")"
~

What am I doing wrong?

3

~ in quotes stays literal ~. The relevant part of Bash Reference Manual starts with

If a word begins with an unquoted tilde character (~), …

To see the difference, compare:

file="~/.gitconfig"
echo "$file"
file=~/".gitconfig"
echo "$file"

In your first example $(…) works first and in its context ~ is unquoted. So it gets expanded as you expect.

Your $file, when it "doesn't work" contains literal ~. POSIX standard says:

The order of word expansion shall be as follows:

Tilde expansion […], parameter expansion […], command substitution […], and arithmetic expansion […] shall be performed, beginning to end. […]

Because tilde expansion is performed before parameter expansion, a variable that expands to ~/something doesn't expand further to a proper path.

Remember ~ or ~/ is special to your shell in some circumstances but to (almost?) any other tool it's not a valid path. When it works, it's because the shell does its "magic" first and the other tool sees the already expanded path (like /Users/jord).

Note in your last example tilde expansion doesn't work, you still get the literal ~ and it's too late for the shell to do something about it without additional tricks (like eval). dirname doesn't complain because it works with strings. It doesn't care if the given path is valid, existent etc. It basically just searches for the last non-slash component and discards it with trailing slashes (if any).

See also this answer.

| improve this answer | |
  • Thank you. This makes perfect sense now you've explained it so well. – jordelver Aug 27 '18 at 9:31

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.