Модуль 02 — Переменные, кавычки, подстановки

Почему "$var" в кавычках — не вкусовщина, а защита от потери файлов

Прогресс курса Модуль 2 из 9

Что вы освоите в этом модуле

01

Присваивание и подстановка

Переменная создаётся присваиванием имя=значение — без пробелов вокруг =. Значение подставляется через $имя или ${имя}.

$ name=Анна
$ echo "Привет, $name"
Привет, Анна
$ name = Анна
-bash: name: команда не найдена

С пробелами bash видит команду name с аргументами = и Анна. Фигурные скобки нужны, когда за именем сразу идут буквы:

$ echo "${HOME}dir" "$HOMEdir|"
/home/studentdir |

$HOMEdir — это переменная с именем HOMEdir, её нет, и подставилась пустая строка. Обращение к несуществующей переменной в bash не ошибка — это частая причина тихих сбоев; в модуле 07 её превратим в ошибку через set -u.

Все переменные bash — строки. Тип появляется только при использовании: в арифметике строка "42" становится числом.

02

Кавычки и разбиение на слова

Значение переменной без кавычек после подстановки делится по пробелам на отдельные слова, а слова с * раскрываются в имена файлов. Обычно это не то, что нужно:

$ 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"
много пробелов
  много   пробелов
03

Подстановка команд и арифметика

$(команда) выполняет команду и подставляет её вывод, без последнего перевода строки. Так результат одной команды становится аргументом другой или значением переменной:

$ 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
04

Аргументы скрипта

Аргументы, переданные скрипту, доступны в специальных переменных:

#!/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.

05

Ввод: read

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 действует только на эту команду.

Скрипт для cron или другого скрипта не должен ничего спрашивать: ответить будет некому. Интерактивный read — для ручных утилит; для автоматических — аргументы и файлы настроек.
06

Окружение и export

Переменная видна только в текущей оболочке. Чтобы её получили запущенные из неё программы, переменную экспортируют в окружение:

$ 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.

07

Значения по умолчанию и операции со строками

Внутри ${…} 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" — длиннее, зато понятнее новичку.

08

Вывод: echo и printf

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%.*} — обработка строк без внешних команд
Практические задания — на учебном портале. Задания, критерии оценивания, сдача работ и оценки преподавателя — в курсе на portal.nevabit.ru. Учётную запись выдаёт преподаватель.
Модуль 01: Первый скрипт Все модули Модуль 03: Условия