~ nas cotações permanece literal ~ . A parte relevante do Manual de Referência de Bash começa com
If a word begins with an unquoted tilde character (
~), …
Para ver a diferença, compare:
file="~/.gitconfig"
echo "$file"
file=~/".gitconfig"
echo "$file"
No seu primeiro exemplo, $(…) funciona primeiro e, no contexto, ~ não é citado. Então, ele se expande conforme o esperado.
Seu $file , quando "não funciona", contém literal ~ . O padrão POSIX diz :
The order of word expansion shall be as follows:
Tilde expansion […], parameter expansion […], command substitution […], and arithmetic expansion […] shall be performed, beginning to end. […]
Como a expansão do til é executada antes da expansão do parâmetro, uma variável que se expande para ~/something não se expande ainda mais para um caminho adequado.
Lembre-se de que ~ ou ~/ é especial para o seu shell em algumas circunstâncias, mas para (quase?) qualquer outra ferramenta, não é um caminho válido. Quando funciona, é porque o shell faz sua "mágica" primeiro e a outra ferramenta vê o caminho já expandido (como /Users/jord ).
Note em seu último exemplo que a expansão de til não funciona , você ainda recebe o literal ~ e é muito tarde para o shell fazer algo a respeito sem truques adicionais (como eval ). dirname não reclama porque trabalha com strings. Não importa se o caminho fornecido é válido, existente, etc. Ele basicamente procura apenas o último componente não-barra e descarta-o com barras finais (se houver).
Veja também esta resposta .