Почему "$var" в кавычках — не вкусовщина, а защита от потери файлов
Переменная создаётся присваиванием имя=значение — без пробелов вокруг =. Значение подставляется через $имя или ${имя}.
$ name=Анна
$ echo "Привет, $name"
Привет, Анна
$ name = Анна
-bash: name: команда не найдена
С пробелами bash видит команду name с аргументами = и Анна. Фигурные скобки нужны, когда за именем сразу идут буквы:
$ echo "${HOME}dir" "$HOMEdir|"
/home/studentdir |
$HOMEdir — это переменная с именем HOMEdir, её нет, и подставилась пустая строка. Обращение к несуществующей переменной в bash не ошибка — это частая причина тихих сбоев; в модуле 07 её превратим в ошибку через set -u.
Все переменные bash — строки. Тип появляется только при использовании: в арифметике строка "42" становится числом.
Значение переменной без кавычек после подстановки делится по пробелам на отдельные слова, а слова с * раскрываются в имена файлов. Обычно это не то, что нужно:
$ file="мой отчёт.txt"
$ ls -l $file
ls: невозможно получить доступ к 'мой': Нет такого файла или каталога
ls: невозможно получить доступ к 'отчёт.txt': Нет такого файла или каталога
$ ls -l "$file"
-rw-rw-r-- 1 student student 0 окт 14 21:56 мой отчёт.txt
Теперь представьте rm $file с таким именем: удалятся файлы мой и отчёт.txt, если они есть, а нужный останется. Поэтому правило: подстановку всегда берите в двойные кавычки, кроме редких случаев, когда разбиение нужно намеренно.
| Запись | Подстановки | Разбиение и шаблоны | Когда |
|---|---|---|---|
"…" | Работают: $var, $(…), $((…)) | Нет | Почти всегда |
'…' | Нет — всё буквально | Нет | Шаблоны для grep, sed, awk; текст с $ |
| без кавычек | Работают | Да | Шаблоны *.log, числа |
$ echo '$HOME' "$HOME"
$HOME /home/student
$ v=" много пробелов "; echo $v; echo "$v"
много пробелов
много пробелов
$(команда) выполняет команду и подставляет её вывод, без последнего перевода строки. Так результат одной команды становится аргументом другой или значением переменной:
$ today=$(date +%F)
$ echo "Сегодня $today"
Сегодня 2026-10-14
$ cp config.yml "config.yml.$(date +%F)" # копия с датой в имени
В старых скриптах встречаются обратные кавычки `date` — это то же самое, но их неудобно вкладывать друг в друга. Пишите $( ).
$((выражение)) вычисляет целочисленное выражение. Переменные внутри можно писать без $:
$ n=7
$ echo $((n * 3 + 1)) $((n / 2)) $((n % 2))
22 3 1
$ x=10; echo "$(( x ** 2 )) $(( x > 3 ))"
100 1
$ (( x++ )); echo $x
11
Деление целочисленное: 7 / 2 = 3. Дробей bash не умеет — для них awk (модуль 06). Ещё одна ловушка — числа с ведущим нулём считаются восьмеричными:
$ echo $(( 08 + 1 ))
-bash: 08: слишком большое значение для основания (неверный маркер «08»)
$ echo $(( 10#08 + 1 )) # явно десятичное — для месяцев и дней из date
9
Аргументы, переданные скрипту, доступны в специальных переменных:
#!/bin/bash
# args.sh — показывает, что получил скрипт
echo "Скрипт: $0"
echo "Аргументов: $#"
echo "Первый: $1, второй: $2"
echo "Все: $*"
for a in "$@"; do echo " [$a]"; done
$ ./args.sh один "два три" четыре
Скрипт: ./args.sh
Аргументов: 3
Первый: один, второй: два три
Все: один два три четыре
[один]
[два три]
[четыре]
| Переменная | Значение |
|---|---|
$0 | Как был вызван скрипт |
$1 … $9, ${10} | Аргументы по номеру |
$# | Число аргументов |
"$@" | Все аргументы, каждый — отдельным словом, как переданы. Используйте его |
"$*" | Все аргументы одной строкой через пробел — только для вывода |
Без кавычек $@ снова разобьётся по пробелам: for a in $@ выдаст четыре аргумента [один] [два] [три] [четыре] вместо трёх. shift сдвигает аргументы: $2 становится $1, а $# уменьшается на 1.
read читает строку из стандартного ввода в переменную. Опция -r обязательна по привычке: без неё обратная косая черта во вводе пропадёт. -p — приглашение, -s — не показывать ввод (пароли).
#!/bin/bash
read -r -p "Как вас зовут? " name
echo "Здравствуйте, ${name:-гость}!"
read умеет разбивать строку на поля по разделителю из переменной IFS:
$ str="a,b,c"
$ IFS=, read -r one two three <<< "$str"
$ echo "$two"
b
<<< («here-string») подаёт строку на вход команде. IFS=, перед read действует только на эту команду.
read — для ручных утилит; для автоматических — аргументы и файлы настроек.Переменная видна только в текущей оболочке. Чтобы её получили запущенные из неё программы, переменную экспортируют в окружение:
$ GREETING=Привет
$ bash -c 'echo [$GREETING]'
[]
$ export GREETING
$ bash -c 'echo [$GREETING]'
[Привет]
$ LANG=C ls nofile # переменная только для одной команды
ls: cannot access 'nofile': No such file or directory
Обратного пути нет: скрипт, запущенный как ./script, работает в отдельном процессе и не может поменять переменные или текущий каталог вызвавшей оболочки. Если нужно именно это — файл подключают командой source файл (или . файл): его команды выполняются в текущей оболочке. Так читают файлы настроек: source /etc/backup.conf.
Внутри ${…} bash умеет обрабатывать значение, не вызывая внешних программ:
$ unset city; echo "${city:-Санкт-Петербург}"
Санкт-Петербург
$ f=/var/log/nginx/access.log
$ echo "${f##*/}" "${f%/*}" "${f%.log}.old" "${#f}"
access.log /var/log/nginx /var/log/nginx/access.old 25
$ v=report-2026-10-14.tar.gz
$ echo "${v%%.*}" "${v#*-}" "${v/2026/2027}"
report-2026-10-14 2026-10-14.tar.gz report-2027-10-14.tar.gz
| Запись | Результат |
|---|---|
${var:-умолч} | Значение или «умолч», если пусто или не задано |
${var:?сообщение} | Остановить скрипт с сообщением, если пусто |
${#var} | Длина в символах |
${var#шаблон}, ${var##шаблон} | Убрать с начала самое короткое / самое длинное совпадение |
${var%шаблон}, ${var%%шаблон} | Убрать с конца самое короткое / самое длинное совпадение |
${var/было/стало} | Заменить первое вхождение; // — все |
${var^^}, ${var,,} | В верхний / нижний регистр |
Мнемоника: # на клавиатуре слева от % — режет слева, % — справа. То же самое через внешние команды: basename "$path" .sql.gz и dirname "$path" — длиннее, зато понятнее новичку.
echo хорош для простых сообщений. Для форматированного вывода — printf: он не добавляет перевод строки сам и повторяет шаблон для каждого аргумента.
$ printf "%s\n" один два три
один
два
три
$ printf "%-8s %5d\n" nginx 42 cron 7
nginx 42
cron 7
printf "%.2f" 3.14159 выдаст ошибку «недопустимое число»: дробная часть по правилам ru_RU пишется через запятую. В скриптах, которые обрабатывают числа, задавайте LC_NUMERIC=C или LC_ALL=C. И ширина %-8s считается в байтах: кириллица (2 байта на букву) выровняется неверно.имя=значение без пробелов; подстановка — "$имя" в двойных кавычках$(команда) — вывод команды, $((выражение)) — целочисленная арифметика$1…, $#, и "$@" — в кавычкахread -r — ввод; export — передать переменную дочерним программам; source — выполнить файл в текущей оболочке${var:-умолч}, ${var##*/}, ${var%.*} — обработка строк без внешних команд