Условия и циклы
Проверки [ -f ], [ -z ], -eq — в test-conditions.
Скрипт — текстовый файл с командами оболочки: строка #! выбирает интерпретатор, аргументы приходят в $1, $2 и "$@".
#!/bin/bash # первая строка: чем запускать
set -euo pipefail
остановиться на первой ошибке
chmod u+x backup.sh
разрешить запуск
./backup.sh "my file.txt"
запуск по пути
bash backup.sh
запуск без права x
bash -n backup.sh
проверить синтаксис, не запуская
name="value"; echo "$name"
переменная — в двойных кавычках
for f in "$@"; do echo "$f"; done
все аргументы по одному
Скрипт — текстовый файл с командами, которые ты набрал бы в терминале.
Читает и выполняет его интерпретатор; у bash-скрипта это
bash.
| Команда | Что нужно | Кто выбирает интерпретатор |
|---|---|---|
./backup.sh |
право x |
строка #! |
backup.sh |
право x и каталог скрипта в PATH |
строка #! |
bash backup.sh |
право r |
ты: bash |
Строка #! (shebang) — первая строка вида #!/bin/bash. По ней
ядро узнаёт, какой программе отдать файл, когда его запускают как команду. При
bash backup.sh интерпретатор уже выбран, и эта строка — комментарий.
sh на Ubuntu — это dash: ls -l /bin/sh покажет /bin/sh -> dash. Он
понимает только синтаксис POSIX, и bash-скрипт, запущенный через sh, падает:
Чтобы скрипт запускался по имени, без ./, положи его в ~/bin: если этот
каталог есть, ~/.profile добавляет его в PATH при входе. Новый каталог
заработает со следующего входа.
имя=значение пишется без пробелов вокруг =. Строку name = "x" bash
читает как команду name с аргументами: name: command not found.
$имя подставляет значение. Без кавычек оболочка делит его по пробелам —
разбиение на слова — и раскрывает шаблоны вроде *:
В двойных кавычках значение остаётся одним аргументом, а $имя и
$(команда) подставляются. В одинарных кавычках текст берётся буквально.
$(команда) подставляет вывод команды: today=$(date +%F).
Аргументы скрипта приходят в позиционных параметрах:
| Запись | Что это |
|---|---|
$0 |
имя скрипта, как его вызвали |
$1, $2… |
первый, второй аргумент |
$# |
число аргументов |
"$@" |
все аргументы, каждый — отдельным словом |
"$*" |
все аргументы одной строкой через пробел |
Без кавычек $@ и $* снова делят my file.txt на два слова.
set -euo pipefail в начале скрипта:
-e — выйти, как только команда вернула не 0; проверки в if, &&, ||
скрипт не останавливают;-u — ошибка на необъявленной переменной: опечатка в имени не превратится
в пустую строку, скрипт выйдет с кодом 1;-o pipefail — конвейер падает, если упало любое его звено.Что значат коды и как их проверять — в exit-codes.
Проверки [ -f ], [ -z ], -eq — в test-conditions.
Глубже проверяет shellcheck: кавычки, cd без проверки, $?.
Цикл по выводу ls или find в $( ) делит имена по пробелам. Шаблон и
find -exec передают каждое имя целиком (см. xargs для -print0).
bash: ./backup.sh: Permission denied, код 126: у файла нет права x.
Выполни chmod u+x backup.sh или запусти bash backup.sh.
backup.sh: command not found, код 127: имя без пути оболочка ищет только в
каталогах PATH. Запусти ./backup.sh или положи скрипт в ~/bin.
bash: ./backup.sh: cannot execute: required file not found — ядро не нашло
интерпретатор из строки #!. Либо опечатка в пути, либо файл пришёл из
Windows с концами строк CRLF: file backup.sh напишет
with CRLF line terminators. Убери \r: sed -i 's/\r$//' backup.sh.
Bash-скрипт выполнил sh (dash): его запустили как sh backup.sh или в строке
#! стоит /bin/sh. Запускай ./backup.sh с #!/bin/bash или
bash backup.sh.
Пробелы вокруг =: name = "x". Пиши name="x".
backup.sh: line 3: name: unbound variable — set -u поймал переменную без
значения, часто опечатку. Исправь имя или дай значение по умолчанию:
"${name:-}".
Не закрыт блок или кавычка: if без fi, do без done, лишняя ". Номер
строки указывает на конец файла — ищи выше.
Переменная без кавычек: cp $f /backup/ получает два аргумента вместо
одного. Пиши "$f", "$@"; shellcheck найдёт такие места (SC2086).
У cron и служб systemd другое окружение: короткий PATH, каталог /, нет
переменных из ~/.bashrc. Подробно — в
окружении службы.
rm -rf "$dir/"* при пустой dir превращается в rm -rf /*. Ставь set -u
или пиши "${dir:?}" — пустое значение остановит скрипт с ошибкой.
cd /srv/tmp; rm -rf ./* удалит файлы в текущем каталоге, если cd не
сработал. Пиши cd /srv/tmp || exit 1.
&&, ||, exit.PATH и переменные окружения.>&2.Есть неточность? Сообщить об ошибке в статье