Bash скрипт: аргументы, getopts и set -euo pipefail

Категория: Bash · Язык: Bash · Обновлено:

Аргументы скрипта доступны в переменных $1, $2 и далее, их количество в $#, а все сразу в "$@". Для ключей вроде -v и -n 5 используют встроенную команду getopts. В начале надёжного скрипта пишут set -euo pipefail, чтобы он останавливался при ошибках.

Позиционные параметры

Bash
#!/usr/bin/env bash
echo "Скрипт: $0"
echo "Первый аргумент: $1"
echo "Второй аргумент: $2"
echo "Всего аргументов: $#"
echo "Все аргументы: $@"

Запуск ./args.sh один два выведет один, два и 2. Переменная $0 хранит имя запущенного скрипта.

Переменная

Значение

$0

Имя скрипта

$1 … $9

Аргументы по номерам; после девятого используйте ${10}

$#

Количество аргументов

"$@"

Все аргументы, каждый отдельным словом

$?

Код выхода последней команды

Всегда пишите "$@" в кавычках: тогда аргументы с пробелами не распадутся на части.

Значения по умолчанию и проверка

Bash
#!/usr/bin/env bash
name="${1:-мир}"                      # если аргумента нет, подставить «мир»
if [ $# -lt 1 ]; then
  echo "Использование: $0 имя" >&2
  exit 1
fi
echo "Привет, $name"

Запись ${1:?сообщение} останавливает скрипт с ошибкой, если аргумента нет.

Ключи через getopts

Bash
#!/usr/bin/env bash
verbose=0
count=1
while getopts "vn:h" opt; do
  case "$opt" in
    v) verbose=1 ;;
    n) count="$OPTARG" ;;
    h) echo "Использование: $0 [-v] [-n число] файл"; exit 0 ;;
    *) echo "Неизвестный ключ" >&2; exit 2 ;;
  esac
done
shift $((OPTIND - 1))                 # убрать разобранные ключи, остаются позиционные
file="${1:?Не указан файл}"
echo "verbose=$verbose count=$count file=$file"

В строке "vn:h" двоеточие после буквы означает, что у ключа есть значение: n: принимает -n 5, оно попадает в $OPTARG. Команда getopts понимает только короткие ключи, для длинных вроде --verbose используйте case по $1 и shift.

Строгий режим

Bash
#!/usr/bin/env bash
set -euo pipefail

Флаг

Что делает

-e

Завершить скрипт, если команда вернула ошибку

-u

Считать ошибкой обращение к неопределённой переменной

-o pipefail

Конвейер падает, если ошибка в любой его команде, а не только в последней

Функции и коды выхода

Bash
#!/usr/bin/env bash
check_file() {
  [ -f "$1" ] || { echo "Нет файла: $1" >&2; return 1; }
  echo "Файл найден: $1"
}

check_file "$1" || exit 1

Запрос данных у пользователя

Bash
read -r -p "Продолжить? [y/N] " answer
[ "$answer" = "y" ] || exit 0

Сделать скрипт исполняемым

Bash
chmod +x script.sh
./script.sh -v -n 3 data.txt

Частые вопросы

Чем $@ отличается от $*?

Без кавычек они одинаковы. В кавычках "$@" сохраняет границы аргументов, а "$*" склеивает всё в одну строку.

Как обработать аргументы по очереди в цикле?

for arg in "$@"; do echo "$arg"; done или циклом с shift: while [ $# -gt 0 ]; do ...; shift; done.

Зачем в начале скрипта строка с #!?

Она сообщает системе, чем выполнять файл. Запись #!/usr/bin/env bash находит Bash в PATH, она переносимее, чем #!/bin/bash.