~ 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:
In your first example
$(…) works first and in its context
~ is unquoted. So it gets expanded as you expect.
$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.
~/ 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
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
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.