Окончание перевода Bash Pitfalls. Предыдущие части доступны в блоге «Оболочки» (часть 1, часть 2) и в моём блоге.
Проблема в том, что в интерактивной оболочке Bash эта команда вызовет ошибку:
Это происходит потому, что при установках по умолчанию Bash выполняет подстановку истории команд в стиле csh с использованием восклицательного знака. В скриптах такой проблемы нет, только в интерактивной оболочке.
Очевидное решение здесь не работает:
Можно заключить эту строку в одинарные кавычки:
Но самое подходящее решение здесь — временно выключить параметр
Почему же тогда всегда не пользоваться одиночными кавычками? Представьте, что вы хотите получить информацию об mp3-файлах:
Одинарные кавычки здесь не подходят, поскольку названия песен содержат апострофы в названиях, а использование двойных кавычек приведёт к проблеме с подстановкой истории команд (а если бы в именах файлов ещё и двойные ковычки содержались, получилось бы вообще черте что). Поскольку лично я (Greg Wooledge, автор текста) никогда не использую подстановку истории команд, я просто поместил команду
В Bash'е, так же, как и в других оболочках семейства Bourne shell, есть специальный синтаксис для работы с позиционными параметрами по очереди, но
Вот правильный синтаксис:
Или просто:
уличная магия, благодаря которой каждый аргумент командной строки заключается в двойные кавычки, так что он выглядит как отдельное слово. Другими словами,
Рассмотрим пример:
Этот код напечатает:
Вот как это должно выглядеть:
В некоторых шеллах это работает, но не во всех. Никогда не комбинируйте ключевое слово
Некоторые версии bash позволяют одновременно использовать и
Замена тильды (tilde expansion) происходит только когда символ ~ не окружён кавычками. В этом примере
Экранирование переменных с путями, которые должны быть выражены относительно домашнего каталога, должно производиться с использованием
Определяя локальную переменную в функции,
Поэтому эти команды лучше разделять:
22. echo "Hello World!"
Проблема в том, что в интерактивной оболочке Bash эта команда вызовет ошибку:
bash: !": event not found
Это происходит потому, что при установках по умолчанию Bash выполняет подстановку истории команд в стиле csh с использованием восклицательного знака. В скриптах такой проблемы нет, только в интерактивной оболочке.
Очевидное решение здесь не работает:
$ echo "hi\!" hi\!
Можно заключить эту строку в одинарные кавычки:
echo 'Hello World!'
Но самое подходящее решение здесь — временно выключить параметр
histexpand
. Это можно сделать командой set +H
или set +o histexpand
:set +H echo "Hello World!"
Почему же тогда всегда не пользоваться одиночными кавычками? Представьте, что вы хотите получить информацию об mp3-файлах:
mp3info -t "Don't Let It Show" ... mp3info -t "Ah! Leah!" ...
Одинарные кавычки здесь не подходят, поскольку названия песен содержат апострофы в названиях, а использование двойных кавычек приведёт к проблеме с подстановкой истории команд (а если бы в именах файлов ещё и двойные ковычки содержались, получилось бы вообще черте что). Поскольку лично я (Greg Wooledge, автор текста) никогда не использую подстановку истории команд, я просто поместил команду
set +H
в свой .bashrc. Но это вопрос привычки и каждый решает для себя сам.23. for arg in $*
В Bash'е, так же, как и в других оболочках семейства Bourne shell, есть специальный синтаксис для работы с позиционными параметрами по очереди, но
$*
и $@
не совсем то, что вам нужно: после подстановки параметров они становятся списком слов, переданных в аргументах, а не списком параметров по отдельности.Вот правильный синтаксис:
for arg in "$@"
Или просто:
for arg
for arg
соответствует for arg in "$@"
. Заключенная в двойные кавычки переменная "$@"
— это специальная "$@"
преобразуется в список "$1" "$2" "$3"
и т.д. Этот трюк подойдёт в большинстве случаев.Рассмотрим пример:
#!/bin/bash # неправильно for x in $*; do echo "parameter: '$x'" done
Этот код напечатает:
$ ./myscript 'arg 1' arg2 arg3 parameter: 'arg' parameter: '1' parameter: 'arg2' parameter: 'arg3'
Вот как это должно выглядеть:
#!/bin/bash # правильно! for x in "$@"; do echo "parameter: '$x'" done
$ ./myscript 'arg 1' arg2 arg3 parameter: 'arg 1' parameter: 'arg2' parameter: 'arg3'
24. function foo()
В некоторых шеллах это работает, но не во всех. Никогда не комбинируйте ключевое слово
function
со скобками (), определяя функцию.Некоторые версии bash позволяют одновременно использовать и
function
, и ()
, но ни в одной другой оболочке так делать нельзя. Некоторые интерпретаторы, правда, воспримут function foo
, но для максимальной совместимости лучше использовать:foo() { ... }
25. echo "~"
Замена тильды (tilde expansion) происходит только когда символ ~ не окружён кавычками. В этом примере
echo
выведет ~
в stdout, вместо того, чтобы вывести пользовательский домашний каталог.Экранирование переменных с путями, которые должны быть выражены относительно домашнего каталога, должно производиться с использованием
$HOME
вместо ~
."~/dir with spaces" # "~/dir with spaces" ~"/dir with spaces" # "~/dir with spaces" ~/"dir with spaces" # "/home/my photos/dir with spaces" "$HOME/dir with spaces" # "/home/my photos/dir with spaces"
26. local varname=$(command)
Определяя локальную переменную в функции,
local
сама работает как команда. Иногда это может непонятным образом взаимодействовать с остатком строки. Например, если в следующей команде вы хотите получить код возврата ($?) подставленной команды, вы его не получите: код возврата команды local его перекрывает.Поэтому эти команды лучше разделять:
local varname varname=$(command) rc=$?