Путаница интерполяции путаницы

1

Я пытаюсь понять интерполяцию Bash переменных.

Я хочу использовать, readlinkчтобы показать путь, на который указывает символическая ссылка.

Если я использую строку, это работает.

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

По какой-то причине это не работает, когда я пытаюсь использовать переменную вместо строки.

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

Ничего не печатается, кроме пустой строки.

Если я сделаю то же самое, но с dirname(в качестве примера) взамен, интерполяция переменных будет работать так, как я ожидал.

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

Что я делаю неправильно?

jordelver
источник
Почему echo "$(dirname -- "$file")"? Почему не просто dirname -- "$file"?
Камиль Мачоровски,
Скопируйте и вставьте из другого примера :)
jordelver
1
Пожалуйста, посмотрите это: что не так с echo $(stuff)или echo `stuff`?
Камиль Мачоровски

Ответы:

3

~в кавычках остается буквальным ~. Соответствующая часть Bash Reference Manual начинается с

Если слово начинается с символа тильды без кавычек ( ~),…

Чтобы увидеть разницу, сравните:

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

В вашем первом примере $(…)работает первым и в его контексте ~не цитируется. Так что это расширяется, как вы ожидаете.

Ваш $file, когда он "не работает" содержит буквальное ~. Стандарт POSIX гласит :

Порядок расширения слова должен быть следующим:

Расширение тильды […], расширение параметров […], подстановка команд […] и арифметическое расширение […] должны выполняться от начала до конца. [...]

Поскольку раскрытие тильды выполняется до раскрытия параметра, переменная, которая расширяется ~/something, не расширяется дальше по правильному пути.

Помните, ~или ~/в некоторых случаях это особенность вашей оболочки, но (почти?) Для любого другого инструмента это неверный путь. Когда это работает, это потому, что оболочка сначала выполняет свою «магию», а другой инструмент видит уже расширенный путь (например /Users/jord).

Обратите внимание, что в последнем примере расширение тильды не работает, вы все равно получаете литерал, ~и оболочке уже слишком поздно что-то делать без дополнительных уловок (например eval). dirnameне жалуется, потому что работает со строками. Ему все равно, является ли данный путь допустимым, существующим и т. Д. В основном он просто ищет последний компонент без косой черты и отбрасывает его с завершающими косыми чертами (если есть).

Смотрите также этот ответ .

Камиль Мачоровски
источник
Спасибо. Это имеет смысл теперь, когда вы так хорошо это объяснили.
Джордельвер