← Весь справочник

set и shopt

Режимы bash — set -euo pipefail, -x для отладки, noclobber, set -- и опции shopt для шаблонов

set и shopt — встроенные команды, которые включают и выключают режимы самой оболочки. Через set задают «строгий режим» скриптов (set -euo pipefail), трассировку команд (set -x) и позиционные аргументы (set -- …). Через shopt — поведение шаблонов *: пустой результат вместо самого шаблона, рекурсивный **, расширенные шаблоны. Справка — help set и help shopt.

Синтаксис

set -о            # включить опцию (минус)
set +о            # выключить опцию (плюс)
set -o имя        # включить опцию по длинному имени; set +o имя — выключить
set -o            # показать все опции и их состояние
set -- a b c      # заменить позиционные аргументы $1 $2 $3

shopt -s имя      # включить опцию shopt (set)
shopt -u имя      # выключить (unset)
shopt имя         # показать состояние

Опции действуют до конца текущей оболочки (скрипта). В шебанг их тоже можно вписать: #!/bin/bash -eu. Текущие короткие опции лежат в $-.

Опции set

КороткаяДлинная (set -o …)Что делает
-eerrexitзавершить скрипт, как только команда вернула ненулевой код (есть исключения — см. ниже)
-unounsetобращение к незаданной переменной — ошибка и выход
pipefailкод пайплайна — код последней упавшей команды, а не только последней команды
-xxtraceпечатать каждую команду перед выполнением (в stderr, с префиксом $PS4, по умолчанию + )
-vverboseпечатать строки скрипта по мере чтения, до подстановок
-Cnoclobber> не перезаписывает существующий файл; принудительно — >|
-fnoglobне раскрывать шаблоны *, ?, […]
-nnoexecтолько проверить синтаксис, ничего не выполнять (bash -n script.sh)
-aallexportавтоматически экспортировать все новые переменные
-Eerrtraceловушка trap … ERR срабатывает и внутри функций
-Tfunctraceловушки DEBUG и RETURN срабатывают внутри функций
-bnotifyсразу сообщать о завершении фоновых задач
-mmonitorуправление заданиями (fg, bg); включено в интерактивной оболочке
-hhashallзапоминать пути найденных команд (включено по умолчанию)
-Bbraceexpandраскрывать {a,b} и {1..5} (включено по умолчанию)
posixрежим совместимости с POSIX
vi / emacsрежим редактирования командной строки

Позиционные аргументы: set --

КомандаЧто делает
set -- a "b c" d$1=a, $2="b c", $3=d, $#=3
set --очистить все аргументы
set -- "$@" новыйдобавить аргумент в конец
set -- $строкаразрезать строку по $IFS на аргументы (сознательно без кавычек)

Опции shopt

ОпцияЧто делает
nullglobшаблон без совпадений превращается в ничто, а не остаётся строкой *.txt
failglobшаблон без совпадений — ошибка no match, команда не выполняется
globstar** совпадает с файлами и каталогами на любой глубине: **/*.log
dotglob* включает скрытые файлы (имена с точки), кроме . и ..
nocaseglobшаблоны без учёта регистра
extglobрасширенные шаблоны: !(…), @(…), +(…), *(…), ?(…)
nocasematch[[ == ]] и case без учёта регистра
lastpipeпоследняя команда пайплайна выполняется в текущей оболочке (работает в скриптах)
inherit_errexit$(…) наследует set -e
expand_aliasesраскрывать алиасы в скрипте (по умолчанию только в интерактивной оболочке)
login_shellтолько чтение: включена, если оболочка — login shell

Флаги shopt: -s включить, -u выключить, -q тихо проверить (код 0 — включено), -p вывести в виде команд, -o работать с опциями set -o.

Расширенные шаблоны (extglob)

ШаблонСовпадает
!(шаблон)со всем, что не совпадает с шаблоном: rm !(*.conf)
@(a|b)ровно с одним из вариантов: *.@(jpg|png)
?(a)ноль или один раз
*(a)ноль или больше раз
+(a)один или больше раз

Примеры

#!/bin/bash
# «строгий режим» в начале скрипта
set -euo pipefail

# отладить кусок скрипта: включить трассировку и выключить обратно
set -x
cp /etc/hostname /tmp/
set +x

# понятный префикс трассировки: номер строки
PS4='+ ${LINENO}: '
set -x

# запустить скрипт с трассировкой, не меняя его
bash -x ./script.sh
# только проверить синтаксис
bash -n ./script.sh

# не затереть существующий файл случайным >
set -o noclobber
echo данные > /tmp/out.txt
echo новое > /tmp/out.txt     # ошибка: cannot overwrite existing file
echo новое >| /tmp/out.txt    # явная перезапись

# цикл по файлам, который не выполнится ни разу, если файлов нет
shopt -s nullglob
for f in /var/log/*.gz; do
    echo "архив: $f"
done

# все .log на любой глубине
shopt -s globstar
ls /var/log/**/*.log

# все файлы, кроме .conf (extglob включай отдельной строкой до использования)
shopt -s extglob
ls /etc/!(*.conf)

# разобрать строку на поля и обращаться к ним как к $1, $2
set -- $(date '+%Y %m %d')
echo "год $1, месяц $2"

# незаданная переменная под set -u — со значением по умолчанию
echo "${DEBUG:-0}"

Подводные камни

  • set -e срабатывает не везде. Ошибка игнорируется в условии if/while, слева от && и ||, после !, в любой команде пайплайна, кроме последней. Функция, вызванная как f || …, выполняется до конца, даже если команды внутри неё падают.
  • $(…) под set -e не наследует -e. x=$(false; echo ok) даст x=ok. Включи shopt -s inherit_errexit.
  • local x=$(cmd) и echo "$(cmd)" скрывают ошибку cmd — код берётся от local или echo. Отдельное присваивание x=$(cmd) под set -e скрипт завершит.
  • ((i++)) при i=0 под set -e завершает скрипт — код возврата 1. Пиши i=$((i + 1)).
  • Без pipefail ошибка в начале пайплайна теряется. false | true возвращает 0. С set -o pipefail — 1.
  • set -u и пустые массивы. "${arr[@]}" у объявленного пустого массива работает (bash 4.4+), а ${#arr[@]} у необъявленного массива — ошибка unbound variable. Для переменных, которых может не быть, пиши ${VAR:-}.
  • nullglob меняет поведение команд с пустым шаблоном. ls *.txt без совпадений превращается в ls и показывает весь каталог. Включай nullglob в скриптах для циклов, а не глобально в ~/.bashrc.
  • extglob нужно включить на отдельной строке до шаблона. Bash разбирает строку целиком перед выполнением, поэтому shopt -s extglob; ls !(*.log) на одной строке — syntax error near unexpected token '('.
  • Алиасы в скриптах не работают без shopt -s expand_aliases: команда выдаст command not found. В скриптах вместо алиасов пиши функции.

См. также

exit-codes · variables · functions · redirection · shellcheck · env