Когда я открываю приглашение bash и набираю:
$ set -o xtrace
$ x='~/someDirectory'
+ x='~/someDirectory'
$ echo $x
+ echo '~/someDirectory'
~/someDirectory
Я надеялся, что 5-я строка выше пошла бы + echo /home/myUsername/someDirectory
. Есть ли способ сделать это? В моем исходном скрипте Bash переменная x фактически заполняется из данных из входного файла через цикл вроде этого:
while IFS= read line
do
params=($line)
echo ${params[0]}
done <"./someInputFile.txt"
Тем не менее, я получаю аналогичный результат, echo '~/someDirectory'
вместо echo /home/myUsername/someDirectory
.
x='~'; print -l ${x} ${~x}
. Я сдался после того, как немного покопался вbash
руководстве.Ответы:
Стандарт POSIX налагает расширение слов в следующем порядке (подчеркните, мое):
Единственный момент, который нас интересует, это первый: как вы видите, расширение тильды обрабатывается до расширения параметра:
echo $x
, тильда не найдена, поэтому она продолжается.echo $x
,$x
находит и раскрывает, и командная строка становитсяecho ~/someDirectory
.~
персонаж остается как есть.Используя кавычки при назначении
$x
, вы явно просили не расширять тильду и рассматривать ее как обычный символ. Часто упускают из виду, что в командах оболочки вам не нужно заключать в кавычки всю строку, так что вы можете сделать раскрытие прямо во время присваивания переменной:И вы также можете сделать так, чтобы расширение происходило в
echo
командной строке, если оно может произойти до раскрытия параметра:Если по какой-то причине вам действительно нужно повлиять на тильду для
$x
переменной без раскрытия и иметь возможность развернуть ее поecho
команде, вы должны выполнить два раза, чтобы вызвать два раскрытия$x
переменной:Однако следует помнить, что в зависимости от контекста, в котором вы используете такую структуру, это может иметь нежелательный побочный эффект. Как правило, предпочитайте избегать использования чего-либо, требующего,
eval
когда у вас есть другой путь.Если вы хотите конкретно решить проблему тильды в отличие от любого другого вида расширения, такая структура будет более безопасной и переносимой:
Эта структура явно проверяет наличие ведущего
~
и заменяет его на домашний каталог пользователя, если он найден.После вашего комментария это
x="${HOME}/${x#"~/"}"
действительно может удивить кого-то, кто не используется в программировании оболочки, но на самом деле связан с тем же правилом POSIX, которое я цитировал выше.В соответствии со стандартом POSIX удаление кавычек происходит последним, а расширение параметров происходит очень рано. Таким образом,
${#"~"}
оценивается и расширяется задолго до оценки внешних кавычек. По очереди, как определено в правилах расширения параметров :Таким образом, правая сторона
#
оператора должна быть правильно указана или исключена, чтобы избежать расширения тильды.Иными словами, когда интерпретатор оболочки смотрит
x="${HOME}/${x#"~/"}"
, он видит:${HOME}
и${x#"~/"}
должен быть расширен.${HOME}
расширяется до содержимого$HOME
переменной.${x#"~/"}
запускает вложенное расширение:"~/"
анализируется, но, будучи заключенным в кавычки, обрабатывается как литерал 1 . Вы могли бы использовать одинарные кавычки здесь с тем же результатом.${x#"~/"}
Само выражение теперь расширяется, в результате чего префикс~/
удаляется из значения$x
.${HOME}
, буквальное/
, расширение${x#"~/"}
.a=$b
я обычно нахожу более ясным добавление двойных кавычек.Кстати, если присмотреться к
case
синтаксису поближе , вы увидите"~/"*
конструкцию, которая опирается на ту же концепцию, чтоx=~/'someDirectory'
я объяснил выше (здесь снова, двойные и простые кавычки могут использоваться взаимозаменяемо).Не волнуйтесь, могут ли эти вещи казаться неясными с первого взгляда (возможно, даже со второго или более позднего взгляда!) По моему мнению, расширение параметров с помощью подоболочек является одной из самых сложных концепций, которые нужно понять при программировании на языке оболочки.
Я знаю, что некоторые люди могут решительно не согласиться с этим, но если вы хотите более подробно изучить программирование оболочки, я советую вам прочитать Руководство по расширенному написанию сценариев Bash : оно учит методам написания сценариев Bash, поэтому с множеством расширений и колокольчиков свистки по сравнению со сценариями оболочки POSIX, но я нашел это хорошо написанным с множеством практических примеров. Когда вам это удастся, легко ограничить себя функциями POSIX, когда вам это необходимо, я лично считаю, что ввод непосредственно в область POSIX - это ненужная крутая кривая обучения для начинающих (сравните мою замену тильды POSIX с регулярным выражением @ m0dular типа Bash эквивалентно, чтобы понять, что я имею в виду;)!).
1 : Что приводит меня к нахождению ошибки в Dash, которая неправильно реализует расширение тильды (проверяется с помощью
x='~/foo'; echo "${x#~/}"
). Расширение параметров - сложное поле как для пользователя, так и для самих разработчиков оболочки!источник
x="${HOME}/${x#"~/"}"
? Похоже конкатенации 3 строк:"${HOME}/${x#"
,~/
и"}"
. Разрешает ли оболочка вложенные двойные кавычки, когда внутренняя пара двойных кавычек находится внутри${ }
блока?Один из возможных ответов:
Поскольку вы читаете ввод из файла, я бы не стал этого делать.
Вы можете найти и заменить ~ значением $ HOME, например так:
Дает мне:
источник
${parameter/pattern/string}
расширение является расширением Bash и может быть недоступно в других оболочках.case
структурой POSIX хорошо иллюстрирует, как сценарии Bash особенно удобны для пользователя. для начинающих.