1 of 48

01

ИПР 2026/27

Bash и

Unix-утилиты

Linux, SSH, grep, find, sed и curl

Лекция 2

2 of 48

План лекции

02

К концу занятия: скрипт, который находит и группирует сообщения об ошибках

Раздел

Результат

Linux и SSH

Рабочая машина и удалённый терминал

Bash

Скрипт, аргументы, условия и потоки

Unix-утилиты

Поиск файлов, текст и HTTP

Разбор логов

Пайплайн, ошибки и практика

3 of 48

Три способа получить Linux

03

Вариант

Где работает

Привычная ОС

Переключение

Основная ОС

На вашем ПК

Заменяется на

выбранном диске

Linux при загрузке

Dual boot

На вашем ПК

Остаётся рядом

Нужна перезагрузка

Облачная ВМ

На сервере

Остаётся на ноутбуке

Подключение по SSH

В примерах используем поддерживаемый выпуск Ubuntu LTS

4 of 48

Подготовка к установке

04

01

Резервная копия

На другой носитель. Проверьте, что нужные файлы открываются.

02

ISO и загрузочная флешка

Официальный образ amd64/x86_64 для обычного ПК Intel/AMD.

03

Запись образа

Запись удаляет данные на флешке.

04

Загрузка и live-среда

Boot Menu, режим UEFI, Try Ubuntu. Проверка Wi-Fi и периферии.

Сценарий для ПК Intel/AMD

5 of 48

Linux как основная система

05

В установщике

После первого входа

Erase disk and install Ubuntu

​

Удаляет прежние разделы

и данные выбранного диска.

​

Перед подтверждением:

модель диска, объём,

итоговый список изменений.

sudo apt update

sudo apt upgrade

update обновляет список пакетов.

upgrade устанавливает обновления.

​

Для обычной работы используем

своего пользователя.

Secure Boot в Ubuntu поддерживается. Отключать его заранее не требуется.

6 of 48

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-раздел сохраняем.

7 of 48

Создание облачной ВМ

07

Настройка

Учебный пример

Образ

Официальный серверный Ubuntu LTS

Ресурсы

2 vCPU, 2 ГБ RAM, диск 20 ГБ*

Сеть

Подсеть и публичный IP для прямого SSH

Входящий доступ

TCP/22 только с вашего публичного IPv4 /32

Пользователь

student и публичный SSH-ключ

* Ориентир для shell. Размер диска должен соответствовать требованиям образа.

8 of 48

SSH-ключи

08

На своём компьютере, в отдельном каталоге вне Git

ssh-keygen -t ed25519 -f ./ipr2026_ed25519 -C "ipr-2026"

ipr2026_ed25519

Приватный ключ хранится у вас

ipr2026_ed25519.pub

Публичную часть добавляем в настройки ВМ

Задайте парольную фразу. Существующий ключ не перезаписываем.

9 of 48

Первое подключение по SSH

09

Команда запускается на ноутбуке

ssh -i ./ipr2026_ed25519 student@<some_ip>

Адрес и логин

Замените учебный IP и student на параметры своей

ВМ

После входа

whoami, hostname и pwd показывают состояние ВМ

10 of 48

Локальная и удалённая работа

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 of 48

Завершение работы в облаке

11

Действие

Что происходит

exit или закрытие ноутбука

SSH-соединение закрывается, ВМ работает

Остановка ВМ

Вычисления останавливаются, диски остаются

Удаление ВМ

Результат зависит от настроек удаления дисков

Проверка ресурсов

Диски, снимки и публичные IP могут остаться платными

Сначала сохраняем результаты, затем проверяем ресурсы в панели провайдера

12 of 48

Первый 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 of 48

Переменные и кавычки

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 of 48

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

14

$(...) получает stdout

$((...)) вычисляет число

current_directory=$(pwd)

printf '%s\n' "$current_directory"

Результат команды становится

значением переменной.

left=10

right=32

sum=$((left + right))

printf '%d\n' "$sum"

# 42

Целочисленная арифметика.

Обычное имя переменной пишем без $.

15 of 48

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

15

if (( $# == 0 )); then

printf 'Usage: %s NAME...\n' "$0" >&2

exit 2

fi

for name in "$@"; do

printf 'Hello, %s!\n' "$name"

done

$# считает аргументы. "$@" сохраняет границы каждого аргумента.

16 of 48

if проверяет статус команды

16

if mkdir -p build; then

printf 'Directory is ready\n'

else

printf 'Cannot create directory\n' >&2

exit 1

fi

0

Команда завершилась успешно

Ненулевой статус

Ошибка или специальный результат команды

$? относится только к последней выполненной команде

17 of 48

Три формы условий

17

Форма

Для чего

Пример

[ ... ]

Команда test

[ "$count" -gt 10 ]

[[ ... ]]

Строки и шаблоны Bash

[[ $file == *.log ]]

(( ... ))

Целочисленная арифметика

(( count > 10 ))

Проверка файла

Каталог / обычный файл

[[ -d $path ]]

[[ -f $path ]]

[[ 10 < 2 ]] и (( 10 < 2 )) отвечают на разные вопросы

18 of 48

for и имена файлов

18

for file in ./*.log; do

[[ -e $file ]] || continue

printf 'Log file: %s\n' "$file"

done

Shell раскрывает glob

Каждое найденное имя становится отдельным

элементом

"$file" сохраняет имя

Пробел внутри имени остаётся частью аргумента

19 of 48

while читает строки

19

while IFS= read -r line || [[ -n $line ]]; do

printf 'Line: %s\n' "$line"

done < input.txt

IFS=

Сохраняет начальные и конечные пробелы

read -r

Сохраняет обратные косые черты

|| [[ -n $line ]] обрабатывает последнюю строку без завершающего перевода строки

20 of 48

Функции: данные и статус

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 of 48

Стандартные потоки

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 of 48

Операторы перенаправления

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 of 48

Порядок перенаправлений

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 of 48

Связывание команд

24

Запись

Когда выполняется B

A && B

Если A завершилась со статусом 0

A || B

Если A завершилась с ненулевым статусом

A; B

После A независимо от её статуса

A | B

B читает stdout команды A

Для разветвлённой обработки ошибок удобнее явный if

25 of 48

Пайплайны и pipefail

25

Обычное поведение

С pipefail

false | true

printf '%s\n' "$?"

# 0

Статус конвейера определяет

последняя команда.

set -o pipefail

false | true

printf '%s\n' "$?"

# 1

Самый правый ненулевой статус,

либо 0, если все успешны.

set -e имеет исключения. Он не заменяет явную обработку ошибок.

26 of 48

Документация и 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 of 48

Просмотр и подсчёт текста

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

28 of 48

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 включает числовое сравнение.

29 of 48

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.

30 of 48

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 пропускает запуск при пустом входе

31 of 48

Регулярные выражения 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 имеет другие правила.

32 of 48

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.

33 of 48

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.

34 of 48

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 разрешает две повторные попытки после первой

35 of 48

Проверка 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 of 48

Задача: частые ошибки в логах

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 of 48

Пайплайн анализа логов

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 of 48

Результат по стадиям

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 of 48

Пустой результат и ошибка

39

01

Вход скрипта

Один аргумент, существующий каталог, понятное сообщение в stderr.

02

Нет файлов или совпадений

Заранее определяем, считается ли это успешным пустым отчётом.

03

Ошибка чтения или выполнения

Не скрываем её общим || true. Проверяем статусы стадий.

04

Раннее завершение head

С pipefail предыдущая команда может сообщить SIGPIPE.

Скрипт проверяем на обычных логах, пустом каталоге и именах с пробелами

40 of 48

Практика: файлы и HTTP

41

Поиск больших логов

Health check

gzip -c "$file" >"$file.gz"

Файлы *.log за последнюю неделю,

размером больше 100 MiB.

​

Сначала показать список.

Затем сохранить gzip-копии,

оставив исходные файлы.

Скрипт принимает URL.

​

Не больше трёх попыток.

Тайм-аут соединения и всей попытки.

Успех в stdout, ошибка в stderr.

​

exit 0 при успехе,

ненулевой статус после неудач.

Обычный gzip file.log по умолчанию заменяет исходный файл

41 of 48

Файлы и права доступа

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

Изменить владельца и группу

42 of 48

Дополнительные формы циклов

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.

​

Тело работает, пока условие

завершается неуспешно.

43 of 48

Практика: окружение и логи

40

01

Рабочее окружение

Назвать пользователя, машину, дистрибутив и текущий каталог.

02

hello.sh

Создать ~/ipr-lab и запустить первый Bash-скрипт.

03

Для облачной ВМ

Скопировать файл через scp, отсоединиться от tmux и вернуться.

04

Скрипт анализа

Принять каталог, найти *.log и вывести топ-20 сообщений ERROR/FATAL.

У скрипта: проверка аргументов, stderr для ошибок, имена с пробелами

44 of 48

Проверка понимания

42

1

Почему после ssh меняется hostname?

2

Сколько аргументов получится из "$@"?

3

Чем grep с кодом 1 отличается от ошибки чтения?

4

Почему uniq без sort может дать другой результат?

45 of 48

После лекции

43

Linux-окружение

Понимаем, на какой машине выполняется команда

Bash-скрипт

Сохраняем границы аргументов и обрабатываем статусы

Unix-утилиты

Выбираем файлы, фильтруем и преобразуем текст

Проверяемый пайплайн

Можем объяснить результат каждой стадии

Далее идут справочные слайды и дополнительные примеры

46 of 48

Установка: особые случаи

A1

Ситуация

Что делать

BitLocker

Сохранить ключ. По требованию установщика дождаться расшифровки.

EFI System Partition

Подключить как /boot/efi без форматирования.

Нет меню GRUB

Проверить Ubuntu и Windows Boot Manager в Boot Menu UEFI.

Диск не виден, RST/RAID

Изучить инструкцию перехода на AHCI до изменения режима.

Apple Silicon

Проверить поддержку конкретной модели проектом Asahi.

Две ОС на одном диске вместе зависят от его исправности

47 of 48

Диагностика SSH

A2

Симптом

Проверка

Connection timed out

Состояние ВМ, публичный IP, сеть и правило TCP/22 с вашего IP

Connection refused

SSH-сервер, нужный порт и firewall с явным отказом

Permission denied (publickey)

Логин, приватный ключ и соответствующая публичная часть на ВМ

HOST IDENTIFICATION HAS CHANGED

Пересоздание ВМ или смена владельца IP. Сверка нового отпечатка.

known_hosts меняем после проверки причины и нового отпечатка

48 of 48

Практика регулярных выражений

A7

1

Строка только из одной или более строчных букв

2

Строка только из одной или более заглавных букв

3

Повторяющиеся пары «буква–цифра»: a1b4G5

4

IPv4-подобная запись из четырёх числовых групп

5

В начале INFO, WARN, ERROR или FATAL и пробел