Файл с командами, который запускается как программа
Скрипт — текстовый файл с командами оболочки, которые выполняются по порядку, как если бы вы набирали их сами. Всё, что работает в терминале, работает и в скрипте; плюс появляются переменные, условия, циклы и функции.
Bash хорош для склейки системных команд: файлы, процессы, службы, архивы, запуск утилит. Если в задаче сложные структуры данных, вычисления или работа с JSON и HTTP API, удобнее Python — граница проходит примерно на сотне строк сложной логики.
Создайте файл hello.sh в любом редакторе (nano hello.sh):
#!/bin/bash
# hello.sh — первый скрипт: кто я, где я, сколько места
echo "Пользователь: $USER"
echo "Каталог: $(pwd)"
echo "Свободно на /: $(df -h --output=avail / | tail -1)"
Первая строка #!/bin/bash называется shebang. Когда файл запускается как программа, ядро читает её и передаёт файл указанному интерпретатору. Остальные строки, начинающиеся с #, — комментарии. $USER — переменная, $(команда) подставляет вывод команды; подробно — в модуле 02.
$ bash hello.sh # 1. явно указать интерпретатор
Пользователь: student
Каталог: /home/student
Свободно на /: 24G
$ ./hello.sh # 2. как программу — нужны права
-bash: ./hello.sh: Отказано в доступе
$ chmod +x hello.sh
$ ./hello.sh
Пользователь: student
Каталог: /home/student
Свободно на /: 24G
Без права x файл нельзя запустить как программу — только передать интерпретатору. chmod +x делается один раз.
Без ./ оболочка ищет команду не в текущем каталоге, а в каталогах из переменной PATH:
$ hello.sh
-bash: hello.sh: команда не найдена
$ echo "$PATH"
/usr/local/bin:/usr/bin:/bin:/usr/games
Текущего каталога в PATH нет намеренно: иначе злоумышленник мог бы положить в /tmp файл с именем ls, и вы запустили бы его вместо настоящей команды. ./ явно говорит: «запусти файл отсюда».
Чтобы скрипт запускался по имени из любого каталога, его кладут в один из каталогов PATH:
| Каталог | Для чего |
|---|---|
/usr/local/bin | Скрипты для всех пользователей сервера, ставятся через sudo install -m 755 backup.sh /usr/local/bin/backup |
~/.local/bin | Свои скрипты; в Debian попадает в PATH через ~/.profile, если каталог существует при входе |
/usr/bin | Только для программ из пакетов — руками туда не кладут |
Расширение .sh для Linux ничего не значит. Скрипты, которые ставят как команды, обычно называют без него: backup, а не backup.sh.
В Debian /bin/sh — это не bash, а dash: маленькая быстрая оболочка, которая понимает только стандарт POSIX. В ней нет многих удобств bash.
$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 фев 4 2025 /bin/sh -> dash
Скрипт ниже написан с конструкциями bash, но начинается с #!/bin/sh:
#!/bin/sh
name="мир"
if [[ $name == м* ]]; then echo "начинается на м"; fi
arr=(a b c); echo "${arr[1]}"
$ ./bashism.sh
./bashism.sh: 3: [[: not found
./bashism.sh: 4: Syntax error: "(" unexpected
$ bash ./bashism.sh
начинается на м
b
Правило курса: скрипт начинается с #!/bin/bash и запускается как ./script или bash script, но не sh script — так shebang игнорируется и скрипт выполнит dash. Такие ошибки называют «башизмами»; они же всплывают в контейнерах Alpine, где bash нет вовсе.
Большинство ошибок в скриптах — не от незнания команд, а от того, как оболочка превращает строку в команду. Порядок действий упрощённо такой:
| Шаг | Что происходит | Пример |
|---|---|---|
| 1. Разбор | Строка делится на слова по пробелам с учётом кавычек; находятся |, >, ; | echo "a b" c → echo, a b, c |
| 2. Подстановки | $переменные, $(команды), $((арифметика)) | $USER → student |
| 3. Разбиение | Результат подстановки без кавычек снова делится на слова | модуль 02 |
| 4. Шаблоны | *, ?, […] раскрываются в имена файлов | *.log → a.log b.log |
| 5. Запуск | Первое слово — команда, остальные — её аргументы |
Пробелы — синтаксис, а не оформление. a=1 — присваивание, a = 1 — запуск команды a с аргументами = и 1. [ -f x ] — проверка, [-f x] — команда [-f, которой нет.
ShellCheck — анализатор скриптов: находит забытые кавычки, башизмы под #!/bin/sh, опечатки в переменных и десятки типичных ошибок до того, как скрипт что-то сломает. Ставится из репозитория Debian:
$ sudo apt install shellcheck
$ shellcheck bashism.sh
In bashism.sh line 3:
if [[ $name == м* ]]; then echo "начинается на м"; fi
^---------------^ SC3010 (warning): In POSIX sh, [[ ]] is undefined.
In bashism.sh line 4:
arr=(a b c); echo "${arr[1]}"
^-----^ SC3030 (warning): In POSIX sh, arrays are undefined.
^-------^ SC3054 (warning): In POSIX sh, array references are undefined.
...
У каждого замечания есть код (SC3010) и страница с объяснением на shellcheck.net/wiki/SC3010. Тот же анализатор подключается к VS Code (расширение ShellCheck) и подчёркивает ошибки прямо при наборе. Правило курса: скрипт сдаётся без замечаний ShellCheck или с объяснением, почему замечание неприменимо.
Скрипт читают чаще, чем пишут: вы сами через полгода, коллега ночью во время аварии. Минимальный порядок оформления:
#!/bin/bash
# disk-report — отчёт о занятом месте по файловым системам.
# Запуск: disk-report [ПОРОГ_%] (по умолчанию 80)
# Код возврата: 0 — всё в норме, 1 — есть заполненные выше порога.
threshold=${1:-80}
df -h --output=pcent,target -x tmpfs -x devtmpfs
\ в конце.threshold, backup_dir. Заглавными — переменные окружения (PATH, HOME), чтобы случайно их не затереть./bin/bash^M: bad interpreter или $'\r': команда не найдена. В VS Code переключите окончания на LF (в строке состояния внизу), на сервере исправьте sed -i 's/\r$//' script.sh.#!/bin/bash выбирает интерпретаторchmod +x и ./script, или bash script; по имени — из каталога в PATHsh — это dash: конструкции bash в нём не работаютa=1 и a = 1 — разные вещи