01
ИПР 2026/27
Bash и
Unix-утилиты
Linux, SSH, grep, find, sed и curl
Лекция 2
План лекции
02
К концу занятия: скрипт, который находит и группирует сообщения об ошибках
Раздел | Результат |
Linux и SSH | Рабочая машина и удалённый терминал |
Bash | Скрипт, аргументы, условия и потоки |
Unix-утилиты | Поиск файлов, текст и HTTP |
Разбор логов | Пайплайн, ошибки и практика |
Три способа получить Linux
03
Вариант | Где работает | Привычная ОС | Переключение |
Основная ОС | На вашем ПК | Заменяется на выбранном диске | Linux при загрузке |
Dual boot | На вашем ПК | Остаётся рядом | Нужна перезагрузка |
Облачная ВМ | На сервере | Остаётся на ноутбуке | Подключение по SSH |
В примерах используем поддерживаемый выпуск Ubuntu LTS
Подготовка к установке
04
01
Резервная копия
На другой носитель. Проверьте, что нужные файлы открываются.
02
ISO и загрузочная флешка
Официальный образ amd64/x86_64 для обычного ПК Intel/AMD.
03
Запись образа
Запись удаляет данные на флешке.
04
Загрузка и live-среда
Boot Menu, режим UEFI, Try Ubuntu. Проверка Wi-Fi и периферии.
Сценарий для ПК Intel/AMD
Linux как основная система
05
В установщике
После первого входа
Erase disk and install Ubuntu
Удаляет прежние разделы
и данные выбранного диска.
Перед подтверждением:
модель диска, объём,
итоговый список изменений.
sudo apt update
sudo apt upgrade
update обновляет список пакетов.
upgrade устанавливает обновления.
Для обычной работы используем
своего пользователя.
Secure Boot в Ubuntu поддерживается. Отключать его заранее не требуется.
Dual boot с Windows
06
01
Подготовка Windows
Резервная копия, ключ BitLocker, отключение Fast Startup.
02
Место для Linux
Сжать том в Windows и оставить область нераспределённой.
03
Установка рядом
Тот же режим UEFI. Вариант Install Ubuntu alongside Windows.
04
Проверка загрузки
По очереди загрузить обе ОС через GRUB или Boot Menu UEFI.
Erase disk удалит Windows с выбранного диска. При ручной разметке EFI-раздел сохраняем.
Создание облачной ВМ
07
Настройка | Учебный пример |
Образ | Официальный серверный Ubuntu LTS |
Ресурсы | 2 vCPU, 2 ГБ RAM, диск 20 ГБ* |
Сеть | Подсеть и публичный IP для прямого SSH |
Входящий доступ | TCP/22 только с вашего публичного IPv4 /32 |
Пользователь | student и публичный SSH-ключ |
* Ориентир для shell. Размер диска должен соответствовать требованиям образа.
SSH-ключи
08
На своём компьютере, в отдельном каталоге вне Git
ssh-keygen -t ed25519 -f ./ipr2026_ed25519 -C "ipr-2026"
ipr2026_ed25519
Приватный ключ хранится у вас
ipr2026_ed25519.pub
Публичную часть добавляем в настройки ВМ
Задайте парольную фразу. Существующий ключ не перезаписываем.
Первое подключение по SSH
09
Команда запускается на ноутбуке
ssh -i ./ipr2026_ed25519 student@<some_ip>
Адрес и логин
Замените учебный IP и student на параметры своей
ВМ
После входа
whoami, hostname и pwd показывают состояние ВМ
Локальная и удалённая работа
10
На ВМ заранее создаём ~/ipr-lab. На ноутбуке готовим hello.sh.
# На ноутбуке
scp -i ./ipr2026_ed25519 ./hello.sh student@<some_ip>:ipr-lab/
# На ВМ
tmux new -s ipr
# На ВМ после нового входа по SSH
tmux attach -t ipr
Ctrl+b, затем d отсоединяет клиента. Сессия живёт, пока работает ВМ.
Завершение работы в облаке
11
Действие | Что происходит |
exit или закрытие ноутбука | SSH-соединение закрывается, ВМ работает |
Остановка ВМ | Вычисления останавливаются, диски остаются |
Удаление ВМ | Результат зависит от настроек удаления дисков |
Проверка ресурсов | Диски, снимки и публичные IP могут остаться платными |
Сначала сохраняем результаты, затем проверяем ресурсы в панели провайдера
Первый Bash-скрипт
12
hello.sh
Два способа запуска
#!/usr/bin/env bash
printf 'Hello, world!\n'
Shebang выбирает интерпретатор
при запуске файла как программы.
bash hello.sh
chmod u+x hello.sh
./hello.sh
Явный bash читает файл.
./hello.sh требует право исполнения.
Bash удобен для автоматизации уже знакомых команд
Переменные и кавычки
13
Присваивание и аргументы
Что увидит printf
file='my report.txt'
cat "$file"
number=10
printf '%s\n' "$number"
В присваивании пробелов вокруг = нет.
В аргументах обычно пишем "$variable".
name='Ada'
printf '%s\n' '$name'
# $name
printf '%s\n' "$name"
# Ada
Без кавычек подстановка может разбиться на слова и раскрыться как glob
Подстановка команд и арифметика
14
$(...) получает stdout
$((...)) вычисляет число
current_directory=$(pwd)
printf '%s\n' "$current_directory"
Результат команды становится
значением переменной.
left=10
right=32
sum=$((left + right))
printf '%d\n' "$sum"
# 42
Целочисленная арифметика.
Обычное имя переменной пишем без $.
Аргументы скрипта
15
if (( $# == 0 )); then
printf 'Usage: %s NAME...\n' "$0" >&2
exit 2
fi
for name in "$@"; do
printf 'Hello, %s!\n' "$name"
done
$# считает аргументы. "$@" сохраняет границы каждого аргумента.
if проверяет статус команды
16
if mkdir -p build; then
printf 'Directory is ready\n'
else
printf 'Cannot create directory\n' >&2
exit 1
fi
0
Команда завершилась успешно
Ненулевой статус
Ошибка или специальный результат команды
$? относится только к последней выполненной команде
Три формы условий
17
Форма | Для чего | Пример |
[ ... ] | Команда test | [ "$count" -gt 10 ] |
[[ ... ]] | Строки и шаблоны Bash | [[ $file == *.log ]] |
(( ... )) | Целочисленная арифметика | (( count > 10 )) |
Проверка файла | Каталог / обычный файл | [[ -d $path ]] [[ -f $path ]] |
[[ 10 < 2 ]] и (( 10 < 2 )) отвечают на разные вопросы
for и имена файлов
18
for file in ./*.log; do
[[ -e $file ]] || continue
printf 'Log file: %s\n' "$file"
done
Shell раскрывает glob
Каждое найденное имя становится отдельным
элементом
"$file" сохраняет имя
Пробел внутри имени остаётся частью аргумента
while читает строки
19
while IFS= read -r line || [[ -n $line ]]; do
printf 'Line: %s\n' "$line"
done < input.txt
IFS=
Сохраняет начальные и конечные пробелы
read -r
Сохраняет обратные косые черты
|| [[ -n $line ]] обрабатывает последнюю строку без завершающего перевода строки
Функции: данные и статус
20
Результат через stdout
Условие через статус
sum() {
local left=$1
local right=$2
printf '%d\n' "$((left + right))"
}
result=$(sum 10 32)
result содержит 42.
is_even() {
(( $1 % 2 == 0 ))
}
if is_even 42; then
printf 'Even\n'
fi
return завершает функцию.
exit завершает текущий shell.
Статус 0–255 служит сигналом выполнения. Данные передаём отдельно.
Стандартные потоки
21
Обозначение | Назначение | Пример перенаправления |
0 stdin | Входные данные | command < input.txt |
1 stdout | Результат команды | command > output.txt |
2 stderr | Диагностика | command 2> errors.txt |
>> | Добавление в конец | command >> output.txt |
| | stdout слева в stdin справа | grep ERROR app.log | sort |
Обычный pipe передаёт stdout. stderr остаётся отдельным потоком.
Операторы перенаправления
22
Без номера: > означает 1>, а < означает 0<
Оператор | Действие | Пример |
> | stdout → файл: перезапись | command > out.log |
< | Файл → stdin | command < input.txt |
2> | stderr → файл: перезапись | command 2> err.log |
>> | stdout → конец файла | command >> out.log |
2>> | stderr → конец файла | command 2>> err.log |
2>&1 | stderr → туда же, куда stdout | command > all.log 2>&1 |
Порядок перенаправлений
23
Команда | stdout | stderr |
command >output.log 2>&1 | output.log | output.log |
command 2>&1 >output.log | output.log | Прежний stdout, обычно терминал |
2>&1 копирует текущее назначение дескриптора 1 в дескриптор 2
Связывание команд
24
Запись | Когда выполняется B |
A && B | Если A завершилась со статусом 0 |
A || B | Если A завершилась с ненулевым статусом |
A; B | После A независимо от её статуса |
A | B | B читает stdout команды A |
Для разветвлённой обработки ошибок удобнее явный if
Пайплайны и pipefail
25
Обычное поведение
С pipefail
false | true
printf '%s\n' "$?"
# 0
Статус конвейера определяет
последняя команда.
set -o pipefail
false | true
printf '%s\n' "$?"
# 1
Самый правый ненулевой статус,
либо 0, если все успешны.
set -e имеет исключения. Он не заменяет явную обработку ошибок.
Документация и ShellCheck
26
Что запускает Bash
Проверка скрипта
type -a printf
command -v python3
help read
man find
help для builtin.
man для системной документации.
bash -n script.sh
shellcheck script.sh
bash -n проверяет синтаксис.
ShellCheck находит типичные ошибки.
Параметры команд проверяем
для целевой ОС.
Просмотр и подсчёт текста
27
Команда | Результат |
head -n 20 app.log | Первые 20 строк |
tail -n 100 app.log | Последние 100 строк |
tail -f app.log | Новые записи по мере добавления |
wc -l app.log | Количество символов перевода строки |
wc -c app.log | Количество байтов |
wc -m app.log | Количество символов с учётом локали |
Для ротации логов в GNU tail часто используют -F
sort и uniq
28
Исходные строки
Подсчёт повторений
db timeout
upstream refused
db timeout
uniq объединяет только соседние
одинаковые строки.
sort messages.txt \
| uniq -c \
| sort -nr
2 db timeout
1 upstream refused
sort располагает одинаковые строки рядом. -n включает числовое сравнение.
find: поиск файлов
29
find ./logs -type f -name '*.log'
find ./logs -type f -name '*.log' \
-mtime -7 -size +100M
-type f и -name
Обычные файлы с подходящим именем
-mtime -7 и -size +100M
Изменённые менее 7 суток назад и больше 100 MiB
'*.log' передаём find буквально. Без кавычек шаблон сначала раскроет shell.
find: действие для найденных файлов
30
find ./logs -type f -name '*.log' \
-exec grep -nH 'ERROR' {} +
find ./logs -type f -name '*.log' \
-print0 | xargs -0 -r grep -nH 'ERROR'
{} +
Передаёт найденные имена группами как
отдельные аргументы
-print0 и -0
Разделяют имена нулевым байтом
Вторая команда для GNU xargs: -r пропускает запуск при пустом входе
Регулярные выражения ERE
31
Элемент | Смысл | Пример |
^ и $ | Начало и конец строки | ^ERROR |
. и \. | Любой символ / точка | \.log$ |
* + ? | 0+ / 1+ / 0 или 1 повтор | [[:digit:]]+ |
[abc] [^abc] | Символ из набора / вне набора | [^[:space:]]+ |
(…) | | Группа и альтернатива | (ERROR|FATAL) |
{m,n} | От m до n повторений | [[:digit:]]{1,3} |
В лекции используем grep -E и sed -E. Glob *.log имеет другие правила.
grep: строки и статусы
32
Выбор строк
Статус поиска
grep -n 'ERROR' app.log
grep -v 'DEBUG' app.log
grep -E 'ERROR|FATAL' app.log
needle='a[b].txt'
grep -F -- "$needle" files.txt
-E включает ERE.
-F ищет буквальный текст.
0 | Есть совпадения |
1 | Совпадений нет |
>1 | Ошибка |
-q оставляет только статус.
-c считает совпавшие строки.
-C 2 показывает контекст.
grep выбирает строки. Для замены текста используем sed.
sed: преобразование потока
33
sed -E 's/old/new/g' input.txt
sed -E 's/(user_id=)[[:digit:]]+/\1<hidden>/g' app.log
sed -n '10,20p' app.log
sed '/DEBUG/d' app.log
s/…/…/g
Замена всех совпадений в каждой строке
-n, p и d
Управление печатью и удалением строк из потока
Без -i исходный файл сохраняется. sed печатает результат в stdout.
curl: HTTP-запрос
34
curl -fL --retry 2 \
--connect-timeout 5 --max-time 20 \
-o response.html 'https://example.com/'
-fL и -o
Обрабатывать HTTP-ошибки, следовать redirect,
сохранить тело
Тайм-ауты и повторы
Ограничить соединение и длительность каждой
попытки
--retry 2 разрешает две повторные попытки после первой
Проверка HTTP-сервиса
35
if curl -fsS \
--connect-timeout 2 --max-time 5 \
'https://example.com/health' >/dev/null; then
printf 'Service is healthy\n'
else
printf 'Health check failed\n' >&2
fi
Учебный URL заменяем адресом своего сервиса. Успешное тело ответа не печатаем.
Задача: частые ошибки в логах
36
Учебные данные: logs/app.log и logs/worker one.log
2026-09-13T10:00:01Z ERROR db timeout request_id=a1
2026-09-13T10:00:02Z WARN retry scheduled request_id=a2
2026-09-13T10:00:03Z ERROR db timeout request_id=a3
2026-09-13T10:00:04Z FATAL config missing request_id=a4
2026-09-13T10:00:05Z ERROR db timeout request_id=b1
2026-09-13T10:00:06Z ERROR upstream refused request_id=b2
2026-09-13T10:00:07Z ERROR upstream refused request_id=b3
2026-09-13T10:00:08Z INFO ready request_id=b4
Нужно выбрать ERROR/FATAL, убрать время и request_id, посчитать повторения
Пайплайн анализа логов
37
find ./logs -type f -name '*.log' \
-exec grep -hE ' (ERROR|FATAL) ' {} + \
| sed -E 's/^[^[:space:]]+[[:space:]]+//' \
| sed -E 's/[[:space:]]+request_id=[^[:space:]]+//g' \
| sort \
| uniq -c \
| sort -nr \
| head -n 20
Каждую ступень можно запустить отдельно и проверить её вывод
Результат по стадиям
38
После grep и sed: 6 строк
После подсчёта: 3 группы
ERROR db timeout
ERROR db timeout
FATAL config missing
ERROR db timeout
ERROR upstream refused
ERROR upstream refused
Порядок обхода файлов find
может отличаться.
3 ERROR db timeout
2 ERROR upstream refused
1 FATAL config missing
Timestamp и request_id исчезли.
Разные запросы объединились
в одинаковые сообщения.
Проверяем смысл результата, включая число строк и ожидаемые группы
Пустой результат и ошибка
39
01
Вход скрипта
Один аргумент, существующий каталог, понятное сообщение в stderr.
02
Нет файлов или совпадений
Заранее определяем, считается ли это успешным пустым отчётом.
03
Ошибка чтения или выполнения
Не скрываем её общим || true. Проверяем статусы стадий.
04
Раннее завершение head
С pipefail предыдущая команда может сообщить SIGPIPE.
Скрипт проверяем на обычных логах, пустом каталоге и именах с пробелами
Практика: файлы и HTTP
41
Поиск больших логов
Health check
gzip -c "$file" >"$file.gz"
Файлы *.log за последнюю неделю,
размером больше 100 MiB.
Сначала показать список.
Затем сохранить gzip-копии,
оставив исходные файлы.
Скрипт принимает URL.
Не больше трёх попыток.
Тайм-аут соединения и всей попытки.
Успех в stdout, ошибка в stderr.
exit 0 при успехе,
ненулевой статус после неудач.
Обычный gzip file.log по умолчанию заменяет исходный файл
Файлы и права доступа
A5
Команда | Назначение |
mkdir -p project/logs | Создать дерево каталогов |
cp source.txt copy.txt | Скопировать файл |
mv old.txt new.txt | Переместить или переименовать |
chmod u+x script.sh | Добавить исполнение владельцу |
chmod 640 secret.txt | Владелец rw-, группа r--, остальные --- |
sudo chown user:group file.txt | Изменить владельца и группу |
Дополнительные формы циклов
A4
C-подобный for
until
for ((n=0; n<=10; n+=2)); do
printf '%d\n' "$n"
done
Вывод: 0, 2, 4, 6, 8, 10.
Условие управляет продолжением.
n=3
until (( n == 0 )); do
printf '%d\n' "$n"
n=$((n - 1))
done
Вывод: 3, 2, 1.
Тело работает, пока условие
завершается неуспешно.
Практика: окружение и логи
40
01
Рабочее окружение
Назвать пользователя, машину, дистрибутив и текущий каталог.
02
hello.sh
Создать ~/ipr-lab и запустить первый Bash-скрипт.
03
Для облачной ВМ
Скопировать файл через scp, отсоединиться от tmux и вернуться.
04
Скрипт анализа
Принять каталог, найти *.log и вывести топ-20 сообщений ERROR/FATAL.
У скрипта: проверка аргументов, stderr для ошибок, имена с пробелами
Проверка понимания
42
1
Почему после ssh меняется hostname?
2
Сколько аргументов получится из "$@"?
3
Чем grep с кодом 1 отличается от ошибки чтения?
4
Почему uniq без sort может дать другой результат?
После лекции
43
Linux-окружение
Понимаем, на какой машине выполняется команда
Bash-скрипт
Сохраняем границы аргументов и обрабатываем статусы
Unix-утилиты
Выбираем файлы, фильтруем и преобразуем текст
Проверяемый пайплайн
Можем объяснить результат каждой стадии
Далее идут справочные слайды и дополнительные примеры
Установка: особые случаи
A1
Ситуация | Что делать |
BitLocker | Сохранить ключ. По требованию установщика дождаться расшифровки. |
EFI System Partition | Подключить как /boot/efi без форматирования. |
Нет меню GRUB | Проверить Ubuntu и Windows Boot Manager в Boot Menu UEFI. |
Диск не виден, RST/RAID | Изучить инструкцию перехода на AHCI до изменения режима. |
Apple Silicon | Проверить поддержку конкретной модели проектом Asahi. |
Две ОС на одном диске вместе зависят от его исправности
Диагностика SSH
A2
Симптом | Проверка |
Connection timed out | Состояние ВМ, публичный IP, сеть и правило TCP/22 с вашего IP |
Connection refused | SSH-сервер, нужный порт и firewall с явным отказом |
Permission denied (publickey) | Логин, приватный ключ и соответствующая публичная часть на ВМ |
HOST IDENTIFICATION HAS CHANGED | Пересоздание ВМ или смена владельца IP. Сверка нового отпечатка. |
known_hosts меняем после проверки причины и нового отпечатка
Практика регулярных выражений
A7
1
Строка только из одной или более строчных букв
2
Строка только из одной или более заглавных букв
3
Повторяющиеся пары «буква–цифра»: a1b4G5
4
IPv4-подобная запись из четырёх числовых групп
5
В начале INFO, WARN, ERROR или FATAL и пробел