Пара 3: Shell, структура каталогов и текстовые утилиты
90 минут · 3 курс, ML.
Содержание и результат
Безопасно создавать, читать, копировать и искать файлы; понимать кавычки.
План занятия
0–10: контекст и исходная задача. 10–40: устройство и механизмы. 40–55: демонстрация команд. 55–80: лабораторная работа. 80–90: разбор результата и фиксация исправлений.
Практика выполняется в своей учебной папке и на localhost. Подготовка окружения описана в lab/README.md.
Команда — имя и аргументы
Shell разделяет слова до запуска программы.
Пробел обычно разделяет аргументы. Путь с пробелом нужно заключить в кавычки. Двойные кавычки сохраняют подстановку переменных, одинарные передают текст буквально. Звёздочку shell может раскрыть в список имён до запуска программы. Это объясняет, почему rm *.txt получает уже список файлов. -- во многих GNU-утилитах завершает параметры: имя, начинающееся с дефиса, после этого рассматривается как операнд. Не все программы имеют одинаковые флаги; проверяйте man или --help.
Аналогия: упаковка заказа в отдельные коробки; кавычки удерживают слова вместе.
Сначала осмотр, затем изменение
pwd и ls снижают риск работы не в той папке.
Терминал не всегда спрашивает подтверждение. cp копирует, mv перемещает или переименовывает, rm удаляет имя. Удаление в shell обычно не отправляет в корзину Windows. На курсе работаем в ~/devops-course/sandbox и используем rm -i для небольших опытов. Не учите привычку sudo перед любой ошибкой: отказ может говорить о неверном пути. Перед изменением прочитайте команду вслух: что является источником, что целью, есть ли перезапись? История команд полезна, но может содержать введённые секреты, поэтому пароли в аргументах не вводим.
Аналогия: прежде чем переносить коробки, проверьте номер склада.
Поиск и чтение
Ищем имя отдельно от текста внутри файла.
find ищет файловые объекты по условиям: имя, тип, размер. grep ищет совпадения в тексте. head и tail показывают начало и конец, less позволяет читать большой файл без загрузки всего текста на экран. wc -l считает строки. grep по умолчанию использует регулярные выражения; grep -F ищет буквальный текст. Выбор простого инструмента — инженерное решение: не нужен Python-скрипт ради одного поиска. Учебный файл мал, но команды пригодятся для логов и результатов экспериментов. Объясните, что grep не понимает смысл модели и не проверяет JSON как структурированный документ.
Имена и содержимое — разные пространства поиска.
Подробный разбор: Shell, имена файлов и текстовые утилиты
Terminal, shell и утилиты — три разных слоя
Shell превращает командную строку в запуск с аргументами и окружением.
Terminal или terminal emulator обеспечивает взаимодействие через псевдотерминал. Bash, zsh, dash, fish — программы, интерпретирующие команды. POSIX sh задаёт переносимый интерфейс, но /bin/sh не обязан быть Bash: в Ubuntu часто используется dash. Bash добавляет собственные конструкции; zsh тоже имеет особенности, fish не стремится быть POSIX shell. Shebang #!/usr/bin/env bash выбирает interpreter для исполняемого скрипта, а запуск bash script.sh выбирает его явно. Команда cd обычно builtin: дочерний процесс не мог бы изменить текущий каталог родителя. Export меняет окружение будущих детей; обычное присваивание shell-переменной не всегда экспортируется. Alias — удобство интерактивного shell, а не надёжная dependency скрипта. PATH содержит каталоги для поиска внешних программ; command -v помогает увидеть реальное разрешение имени.
Практическое следствие: Кавычки, expansion и разбиение argv происходят до исполнения утилиты. На Mac интерактивный zsh и системный старый Bash — разные среды.
Файл .env скрыт только от обычного списка
В Linux точка — соглашение об имени, а не особый секретный тип файла.
У имени .env нет автоматического запрета чтения: ls без -a обычно не показывает имена, начинающиеся с точки, но cat .env читает их при разрешённом доступе. . — текущий каталог, .. — родительский; это специальные записи, а не обычные пользовательские файлы. .config хранит пользовательские настройки по распространённому соглашению XDG; .bashrc и .zshrc относятся к соответствующим shell. Windows имеет отдельный атрибут hidden, поэтому похожее отображение достигается другим механизмом. Gitignore тоже не защищает секрет: он влияет на выбор неотслеживаемых файлов, но уже закоммиченный .env остаётся в истории. На практике применяют доступ по правам, разделение конфигурации и секретов, исключение из репозитория и ротацию случайно раскрытого секрета. Не ставьте chmod 777 ради чтения конфигурации.
Практическое следствие: ls -a показывает скрытые имена; ls -A опускает специальные записи . и .. Hidden не означает encrypted или unreadable.
/etc, /var, /usr, /tmp: роли каталогов
По роли данных можно выбрать место хранения и область диагностики.
/etc содержит конфигурацию конкретного хоста; современный смысл важнее спорной расшифровки имени. /usr содержит установленный userland: программы, библиотеки и общие данные. /var предназначен для изменяемых данных — журналов, очередей и состояния приложений. /home содержит домашние каталоги; /root — домашний каталог root, а не синоним /. /run хранит состояние текущей загрузки; /tmp — временные файлы. /proc и /sys — интерфейсы виртуальных файловых систем со сведениями ядра, /dev — специальные объекты устройств. На merged-/usr системах /bin и /sbin могут быть ссылками внутрь /usr, поэтому старый рисунок «каждый каталог отдельный диск» неверен. FHS описывает соглашения, но конкретное приложение или контейнер может выбирать другое размещение. Всегда проверяем эффективный config и mounts.
Практическое следствие: Настройки, код, runtime state и пользовательские данные имеют разные сроки жизни и требования к резервному копированию.
grep, sed и awk решают разные задачи
Текстовый поток можно отобрать, преобразовать и разобрать без изменения оригинала.
Grep выбирает строки по совпадению. -F ищет буквальный текст, -E использует расширенное регулярное выражение, -n добавляет номер строки. Sed применяет команды к потоку: s/old/new/ меняет первое совпадение в строке, флаг g — все подходящие совпадения. Без -i результат идёт в stdout, исходный файл сохраняется. Awk работает с записями и полями, умеет фильтрацию и вычисления; его стандартное разделение по whitespace не является полноценным CSV-парсером с кавычками. Sort упорядочивает строки, uniq объединяет соседние одинаковые, поэтому для полного подсчёта обычно нужны sort | uniq -c. Locale влияет на порядок и классы символов; LC_ALL=C полезен, когда нужен определённый байтовый порядок. GNU/BSD варианты флагов различаются. Для JSON выбираем JSON-парсер, а не пытаемся извлечь вложенную структуру регулярным выражением.
Практическое следствие: Фильтрацию и преобразование сначала выводим в новый файл; sed -i используется после проверки и с учётом различий GNU/BSD.
Опыт: связать рисунок с наблюдением
mkdir -p ~/devops-course/text-demo
cd ~/devops-course/text-demo
printf "INFO status=ok\nERROR status=failed\n" > events.log
printf "local-setting\n" > .settings
ls
ls -a
grep -n -F ERROR events.log
sed 's/failed/retry/' events.log
awk '{print $1}' events.log
command -v sh bash grep sed
Сохраните отфильтрованные ERROR в отдельный файл, сравните оригинал и результат. Работающие команды не требуют sudo. Прочитайте man grep / man sed своей ОС перед использованием расширенных флагов.
Источники подробного разбора
Команды и наблюдения
Окружение: Ubuntu / Bash.
mkdir -p ~/devops-course/sandbox
cd ~/devops-course/sandbox
printf "OK run=1
ERROR run=2
OK run=3
" > "run log.txt"
cp -- "run log.txt" backup.txt
grep -n -F ERROR "run log.txt"
find . -type f -name "*.txt"
wc -l "run log.txt"
cmp "run log.txt" backup.txt
Кавычки делают run log.txt одним аргументом; шаблон find заключён в кавычки, чтобы его обработал find, а не shell.
Ожидаемый результат: grep находит строку 2; wc показывает 3; cmp молчит и возвращает 0.
Вариант для macOS
mkdir, cp, grep, find, head, tail, less, wc и cmp доступны. Linux find -maxdepth не считайте переносимым; для поиска по всему учебному дереву достаточно команды выше. BSD и GNU-флаги могут различаться. Проверяйте man на своей системе.
mkdir -p ~/devops-course/sandbox
cp -- "run log.txt" backup.txt
grep -n -F ERROR "run log.txt"
find . -type f -name "*.txt"
wc -l "run log.txt"
Практика: Архив экспериментальных запусков
- Создайте три файла, один с пробелом в имени.
- Скопируйте их в отдельную папку backup без изменения оригиналов.
- Найдите строки ERROR и посчитайте файлы через find.
- Переименуйте один файл и удалите только специально созданную копию с rm -i.
Результат: history-команды без секретов и найденные строки с номерами.
Проверка: Оригиналы сохранены; все пути с пробелами обработаны корректно.
Неисправность для разбора: Неэкранированный пробел разбил имя на два аргумента.
Решение и диагностика
Используйте mkdir -p backup; cp -- ./.txt backup/ до появления новых текстовых файлов в текущей папке. grep -n -F ERROR "run log.txt". Для подсчёта этих учебных имён find . -maxdepth 1 -type f -name ".txt" | wc -l. Поясните ограничение подсчёта по строкам для необычных имён с переводом строки; в этом упражнении такие имена не создаём.
Основные выводы
- Обычно в shell до запуска команды, если шаблон не заключён в кавычки.
- Ищет буквальную строку без интерпретации регулярного выражения.
Самостоятельная работа
Создать шпаргалку из восьми команд: цель, пример, риск неправильного применения.