Практическое написание сценариев командной оболочки Bash/Bash подстановки: различия между версиями

Содержимое удалено Содержимое добавлено
Строка 627:
 
== Особенности интерпретации подстановок ==
 
Подстановки являются очень тонким местом интерпретатора и непонимание общего механизма может приводить к неявным, трудноуловимым ошибкам. В основном все проблемы начинаются тогда, когда в строках появляются символы разделителей IFS.
 
Давайте начнем с общих правил. Интерпретатор, разбирая сценарий построчно, разбивает каждую строку на отбельные слова, используя пробельные символы. Для интерпретатора важно понять, что из результата является командой, а что ее параметрами. В общем случае мы называем такие слова «голыми» (''bare words'').
<source lang="bash">
# Данная команда состоит из 4 голых слов. Из них крайнее левое слово всегда интерпретируется как имя команды,
# а все последующие - это ее параметры. Причем количество пробелов между голыми словами не имеет значения.
command param1 param2 param3
</source>
Из-за того что голые слова разделяются пробелами, их нельзя нигде ставить при присваивании:
<source lang="bash">
# ОШИБКА
VARIABLE = 1234
# С точки зрения интерпретатора, вы ввели команду VARIABLE и передали ей два аргумента: '=' и '1234'
 
# ОШИБКА
VARIABLE=A very long string
# Так тоже делать нельзя. С точки зрения интерпретатора, вы вызвали команду 'very' и передали ей
# экспортированную переменную VARIABLE со значением 'A', а также параметры 'long' и 'string'.
</source>
Для преодоления таких проблем, когда пробелы нужно интерпретировать не как разделители голых слов, в интерпретаторе предусмотрены символы кавычек двух типов. Первый тип — это двойные кавычки, которые не дают интерпретатору использовать пробелы как разделители голых слов. При этом внутри двойных кавычек интерпретатор может разрешать встречающиеся ему простые подстановки.
<source lang="bash">
VARIABLE="A very long string"
# Двойные кавычки не позволяют интерпретатору разбить правую часть от равно на голые слова.
 
# ДОПУСТИМО
VARIABLE_1=$VARIABLE
# Несмотря на то что в оригинальной переменной есть пробелы, процедура присваивания не будет принимать их во внимание после подстановки, т.е.
# кавычки ставить не обязательно.
 
echo "Value: $VARIABLE_1"
# Интерпретатор разрешает подстановки внутри двойных кавычек.
</source>
Второй тип — это одинарные кавычки. Одинарные кавычки выполняют ту же задачу что и двойные, но еще и запрещают разрешать подстановки.
<source lang="bash">
echo 'Cost: $10'
echo 'Total cost: 25 000 $'
# Так как знак '$' несет особый смысл (интерпретатор определяет так начало подстановки), мы используем одинарные кавычки, чтобы запретить его интерпретацию.
# В этом случае мы говорим, что знак '$' понимается буквально.
</source>
Кроме кавычек, для запрета интерпретации одиночных специальных символов используется еще один символ — обратный слеш <code>\</code>. Использование такого слеша иногда называют ''экранированием символа''.
<source lang="bash">
echo "Cost: \$10"
echo "Total cost: 25 000 \$"
 
# В этом примере мы используем двойные кавычки, но экранируем символы '$'.
</source>
 
На первый взгляд все просто, однако на практике очень часто допускают ошибки, когда подстановки передаются в команды. Для демонстрации ошибок мы будем использовать простую функцию печати аргументов.
<source lang="bash">
arg_printer() {
echo "Arg counter: $#"
local -i counter
for arg; do
echo "$((counter++)): '$arg'"
done
}
</source>
Когда подстановка передается команде без двойных кавычек, и если в подставляемом значении есть пробелы, то по этим пробелам подставляемый результат будет разбит на соответствующие голые слова.
<source lang="bash">
LONG_STRING="A Very Long String"
arg_printer $LONG_STRING # Подстановка будет разбита на 4 голых слова, т.е. команде будет передано 4 параметра
# Результат:
# Arg counter: 4
# 0: 'A'
# 1: 'Very'
# 2: 'Long'
# 3: 'String'
 
# Если мы передаем подстановку в двойных кавычках, то мы запретим разбивать по пробелам на голые слова.
arg_printer "$LONG_STRING"
# Результат:
# Arg counter: 1
# 0: 'A Very Long String'
 
# В следующем примере мы склеиваем несколько строк вместе. Обратите внимание, что каждый пробел в склеиваемых
# строках будет участвовать в разделении на голые слова, если он не закавычен.
ADDITION_STRING="with some additions"
SPACE=' '
arg_printer "$LONG_STRING"$SPACE$ADDITION_STRING
# Результат:
# Arg counter: 4
# 0: 'A Very Long String'
# 1: 'with'
# 2: 'some'
# 3: 'additions'
</source>
 
Для некоторых выражений в Bash очень важно правильно разбивать строки на голые слова. Одним из таких примеров является цикл <code>for</code>.
<source lang="bash">
LIST="Apple Pear Potato Tomato"
 
# НЕПРАВИЛЬНО
for item in "$LIST"; do
echo "$item"
done
# Результат:
# Apple Pear Potato Tomato
#
# В данном случае ставить двойные кавычки неправильно, так как они мешают сформировать список и склеивают
# все элементы в одну большую строку.
#
# ПРАВИЛЬНО
for item in $LIST; do
echo "$item"
done
# Результат:
# Apple
# Pear
# Potato
# Tomato
</source>
Однако проблемы начинаются, когда в элементах появляются пробелы как часть значения. Эти пробелы участвуют в разбиении на голые слова, в результате чего мы работаем не с теми значениями, которые имеем в виду на самом деле.
<source lang="bash">
# Пусть у нас есть пути к файлам, в именах которых есть пробелы.
ITEM_1="/home/john/files/My document.txt"
ITEM_2="/home/alice/music collection/jazz/Louis Armstrong"
 
# Тогда писать так было бы НЕПРАВИЛЬНЫМ
for item in $ITEM_1 $ITEM_2; do
echo "$item"
done
</source>
 
{{Нижняя автоматическая навигация|next=Команды|prev=Эмуляция ссылочной адресации}}