Блок 6

Bash углублённо: конвейеры, перенаправления, обработка текста

Linux: блок 6

Вы уже уверенно работаете в терминале и владеете базовыми командами файловой системы. Настало время перейти к тому, что делает Linux по-настоящему мощным инструментом — способности комбинировать пр…

Обновлено: · DeeNet

Введение в блок

Вы уже уверенно работаете в терминале и владеете базовыми командами файловой системы. Настало время перейти к тому, что делает Linux по-настоящему мощным инструментом — способности комбинировать простые команды друг с другом, строя из них сложные цепочки обработки данных. Именно этот принцип мы упоминали ещё в уроке 1.1 как одну из ключевых идей UNIX: маленькие программы, каждая из которых хорошо делает одну вещь, и их можно соединять между собой.

В этом блоке вы научитесь перенаправлять вывод команд в файлы, передавать результат одной команды на вход другой, искать текст внутри файлов, обрабатывать и преобразовывать текстовые данные — и всё это без единой строчки «настоящего» программирования. Материал этого блока — прямая подготовка к Блоку 15, где вы будете писать полноценные Bash-скрипты, объединяя все эти инструменты в автоматизированные сценарии.

Урок 6.1. Стандартные потоки ввода/вывода/ошибок (stdin, stdout, stderr)

Понять фундаментальную концепцию, на которой строятся все перенаправления и конвейеры, изучаемые в этом блоке — без понимания «потоков» дальнейший материал будет восприниматься как набор магических символов, а не как логичная, продуманная система.

Теория

Когда мы говорили в предыдущих уроках, что команда «выводит текст в терминал» (например, echo, cat, ls), мы упрощали картину. На самом деле у каждой запущенной программы (процесса, подробно о процессах — в Блоке 10) в Linux по умолчанию открыты три отдельных потока (streams) данных — что-то вроде трёх независимых «труб», по которым может течь информация.

Стандартный ввод (stdin, standard input) — поток, откуда программа получает входные данные. По умолчанию это клавиатура: когда программа «ждёт», что вы что-то введёте, она читает именно из stdin.

Стандартный вывод (stdout, standard output) — поток, куда программа отправляет свой обычный, «нормальный» результат работы. По умолчанию это экран терминала — именно поэтому, когда вы вводите echo "Привет", текст «Привет» появляется прямо на экране: он отправляется в stdout, а stdout по умолчанию подключён к экрану.

Стандартный вывод ошибок (stderr, standard error) — отдельный, независимый поток, куда программа отправляет именно сообщения об ошибках и предупреждениях. По умолчанию stderr тоже подключён к экрану терминала, поэтому визуально сообщения об ошибках выглядят так же, как обычный вывод — но технически это два совершенно разных, независимых потока, и это разделение оказывается чрезвычайно полезным на практике, как мы увидим в следующем уроке.

Почему вообще разделили обычный вывод и вывод ошибок на два разных потока, если по умолчанию оба показываются на одном и том же экране? Представьте ситуацию: вы хотите сохранить результат работы программы в файл (тема следующего урока), но при этом сообщения об ошибках хотите по-прежнему видеть сразу на экране, а не «терять» их внутри файла с результатами. Именно раздельность потоков stdout и stderr делает такую избирательную обработку возможной — вы можете перенаправить каждый поток по отдельности, в разные места.

Каждый из трёх потоков в Linux имеет свой числовой идентификатор, который используется при работе с перенаправлениями (подробно — в следующем уроке): stdin — это дескриптор 0, stdout — дескриптор 1, stderr — дескриптор 2. Эти числа станут понятнее и практически применимыми уже в следующем уроке, когда мы начнём использовать конкретные символы перенаправления.

Практика

Практика этого урока — наблюдательная, чтобы на конкретных примерах увидеть разницу между stdout и stderr, прежде чем в следующем уроке мы научимся ими управлять.

Шаг 1. Откройте терминал и выполните заведомо успешную команду, например ls ~ (вывод отправится в stdout).

Что вы увидите: обычный список файлов вашей домашней папки — это пример типичного использования потока stdout.

Шаг 2. Выполните заведомо ошибочную команду, обращённую к несуществующей папке: ls /nesuschestvuyuschaya_papka.

Что вы увидите: сообщение вида ls: cannot access '/nesuschestvuyuschaya_papka': No such file or directory — это пример сообщения, отправленного именно в поток stderr, а не в stdout, хотя визуально оба случая выглядят как «просто текст на экране».

[Скриншот терминала]

Шаг 3. Пока не пытайтесь ничего перенаправлять — просто зафиксируйте в конспекте оба случая (шаг 1 и шаг 2) и отметьте для себя, что в обоих случаях текст появился на экране одинаково, хотя технически это были два разных потока — эту разницу мы сделаем видимой уже в следующем уроке.

Разбор команд

В этом уроке мы не вводим новых команд — используем уже знакомую ls для иллюстрации концепции. Собственно инструменты для управления потоками (символы перенаправления) подробно разберём в следующем уроке.

Возможные ошибки

Почему возникает: новичок ожидает, что раз stdout перенаправлен в файл, то абсолютно весь текст, включая ошибки, окажется именно там.

Как определить: сообщения об ошибках «просачиваются» на экран терминала, несмотря на явное перенаправление вывода в файл.

Как исправить: осознать, что stdout и stderr — независимые потоки, и простое перенаправление stdout никак не затрагивает stderr, если не указано перенаправить оба потока явно.

Как избежать: заранее усвоить теорию этого урока о раздельности трёх потоков — это ключевое понимание, которое сделает поведение перенаправлений в следующем уроке абсолютно логичным, а не «магическим».

Практические задания

  1. Выполните три любые команды: одну заведомо успешную, одну заведомо ошибочную (обратитесь к несуществующему файлу или папке), и ещё одну, которая, по вашему предположению, тоже приведёт к ошибке — запишите в конспект точный текст сообщений об ошибках, которые вы увидели.
  2. Своими словами (без подглядывания) объясните в конспекте, зачем стандартный вывод и стандартный вывод ошибок разделены на два независимых потока, если оба по умолчанию показываются на одном и том же экране.

Проверка знаний

Вопрос 1. Какой числовой дескриптор соответствует потоку stdout, и какой — потоку stderr?

Ответ: stdout — дескриптор 1; stderr — дескриптор 2 (stdin, для полноты картины, — дескриптор 0).

Вопрос 2. Если оба потока — stdout и stderr — по умолчанию выводятся на один и тот же экран терминала, зачем вообще их разделять технически?

Ответ: Разделение позволяет избирательно управлять каждым потоком по отдельности — например, перенаправить обычный результат работы программы (stdout) в файл, сохранив сообщения об ошибках (stderr) видимыми прямо на экране, или наоборот. Без такого разделения подобная избирательная обработка была бы невозможна.

Итоги урока

Вы изучили: концепцию трёх стандартных потоков данных — stdin, stdout, stderr — и их числовые дескрипторы.

Вы умеете: различать на практике ситуации, где команда использует stdout, а где — stderr.

В следующем уроке потребуется: это понимание, чтобы освоить символы перенаправления потоков и научиться управлять ими.

Урок 6.2. Перенаправление потоков: `>`, `>>`, `<`, `2>`

Научиться направлять вывод команд не на экран, а в файлы (и наоборот, брать входные данные не с клавиатуры, а из файлов) — это один из важнейших практических инструментов автоматизации в Linux, который будет постоянно использоваться в дальнейших блоках курса, особенно при работе с журналами (Блок 10, Блок 18) и написании скриптов (Блок 15).

Теория

Перенаправление (redirection) — это механизм Bash, позволяющий изменить, откуда команда берёт свои входные данные или куда отправляет свой вывод, вместо стандартных значений по умолчанию (клавиатура для ввода, экран для вывода), о которых мы говорили в предыдущем уроке. Мы уже мельком использовали перенаправление в Блоке 5 (урок 5.5, команда echo "текст" > file.txt), теперь разберём этот механизм полноценно.

Символ > — перенаправляет стандартный вывод (stdout) команды в указанный файл, полностью заменяя предыдущее содержимое этого файла (если файл уже существовал) или создавая новый файл (если его ещё не было).

Символ >> — тоже перенаправляет stdout в файл, но, в отличие от одиночного >, дописывает новые данные в конец уже существующего содержимого файла, не удаляя то, что там было раньше. Это принципиально важное различие: одна лишняя черта в символе кардинально меняет поведение команды — с «перезаписать» на «дописать».

Символ < — перенаправляет стандартный ввод (stdin), то есть указывает команде брать входные данные не с клавиатуры, а из указанного файла. Используется реже, чем > и >>, но встречается, например, при подаче на вход команде заранее подготовленного текстового файла с данными для обработки.

Символ 2> — перенаправляет именно поток ошибок (stderr, дескриптор 2, как мы разобрали в предыдущем уроке), оставляя обычный вывод (stdout) идти своим обычным путём (на экран, если не указано иное). Аналогично можно явно указывать 1> для stdout (хотя это избыточно, так как > без числа по умолчанию и так означает именно stdout), а также использовать 2>> для дозаписи именно потока ошибок в файл.

Существует также специальный «файл» /dev/null — так называемое «нулевое устройство» (мы уже видели пример его использования в уроке 5.8, в команде find / -size +1G 2>/dev/null). Технически это не обычный файл, а специальный виртуальный файл-устройство (вспомните урок 5.2 про папку /dev, где физические и виртуальные устройства представлены как файлы), который просто «поглощает» и уничтожает любые данные, направленные в него, ничего никуда фактически не записывая. Перенаправление какого-либо потока в /dev/null — это стандартный, широко распространённый способ «заглушить», подавить ненужный вывод (чаще всего именно поток ошибок), когда он не интересует пользователя, но мешает читать полезный результат.

Практика

Шаг 1. Перейдите в свою учебную папку и создайте новый файл с перенаправлением stdout: echo "Первая строка" > output.txt.

Что произойдёт: будет создан (или полностью перезаписан, если уже существовал) файл output.txt с указанным содержимым — мы уже делали это в Блоке 5, здесь просто закрепляем понимание.

Шаг 2. Допишите ещё одну строку, используя >>: echo "Вторая строка" >> output.txt.

Шаг 3. Просмотрите итоговое содержимое: cat output.txt.

Что вы увидите: обе строки, «Первая строка» и «Вторая строка» — благодаря использованию >> вторая команда дописала свою строку, не удалив первую.

[Скриншот терминала]

Шаг 4. Теперь для сравнения используйте одиночный > вместо >>: echo "Совершенно новая строка" > output.txt, а затем снова cat output.txt.

Что вы увидите: в файле теперь только «Совершенно новая строка» — обе предыдущие строки были полностью стёрты одиночным символом >, наглядно демонстрируя разницу между > и >>.

Шаг 5. Потренируйте перенаправление ошибок. Выполните команду, которая заведомо выдаст и обычный вывод, и ошибку одновременно — для этого попробуем ls сразу с существующим и несуществующим путём: ls ~ /nesuschestvuyuschaya_papka.

Что вы увидите: список файлов домашней папки (stdout) вперемешку с сообщением об ошибке (stderr) — оба потока по-прежнему показываются на одном экране, как мы наблюдали в предыдущем уроке.

Шаг 6. Теперь перенаправьте именно ошибки в отдельный файл, оставив обычный вывод на экране: ls ~ /nesuschestvuyuschaya_papka 2> errors.txt.

Что произойдёт: на экране вы увидите только список файлов домашней папки (stdout по-прежнему идёт на экран), а сообщение об ошибке «незаметно» окажется перенаправлено в файл errors.txt, минуя экран.

Шаг 7. Проверьте содержимое файла ошибок: cat errors.txt.

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

[Скриншот результата]

Шаг 8. Попробуйте полностью подавить ошибку, перенаправив её в /dev/null: ls ~ /nesuschestvuyuschaya_papka 2>/dev/null.

Что произойдёт: на экране вы увидите только успешный список файлов, а сообщение об ошибке будет полностью уничтожено, никуда не сохранившись, — ни на экране, ни в каком-либо файле.

Разбор команд

В этом уроке разбираются не отдельные команды, а специальные синтаксические конструкции (операторы) самого Bash — это важно понимать: символы >, >>, <, 2> не являются отдельными программами (в отличие от ls или cat), а интерпретируются непосредственно самой оболочкой Bash при разборе введённой строки, ещё до фактического запуска указанной команды.

Оператор >

Оператор >>

Оператор <

Оператор 2> (и связанные 1>, 2>>)

Способы исправления: запомнить правильный порядок «сначала перенаправить stdout в файл, затем указать, что stderr следует направить туда же, куда уже направлен stdout» — именно поэтому 2>&1 всегда пишется после > файл, а не до него.

Возможные ошибки

Почему возникает: визуальное сходство одного символа > и двух >>, легко перепутать при быстром наборе.

Как определить: файл, который должен был содержать накопленную историю (например, лог работы скрипта за несколько дней), неожиданно содержит только самую последнюю запись.

Как исправить: восстановление только из резервной копии, если она была сделана заранее (тема Блока 11) — это ещё один практический аргумент в пользу регулярного резервного копирования важных данных.

Как избежать: перед выполнением команды перенаправления в уже существующий важный файл — сознательно проверять, действительно ли нужное поведение — это «перезаписать» (>) или «дописать» (`>>»), особенно если команда вводится не в первый раз, а копируется/адаптируется из истории (урок 4.5).

Практические задания

  1. Создайте файл numbers.txt, дважды дописав в него через >> разные строки текста, а затем просмотрите итоговый результат cat.
  2. Намеренно вызовите команду с ошибкой (например, обращение к несуществующему файлу) и перенаправьте именно поток ошибок в отдельный файл с помощью 2>, оставив обычный вывод команды (если он есть) видимым на экране — просмотрите оба результата: то, что осталось на экране, и содержимое созданного файла ошибок.
  3. Потренируйте полное перенаправление обоих потоков в один файл с помощью конструкции > файл 2>&1 на любой команде, которая одновременно выдаёт и обычный вывод, и ошибку (например, аналогично шагу 6 практики этого урока, но объединив оба потока в один файл вместо разделения).

Проверка знаний

Вопрос 1. Чем отличается поведение символа > от >> при перенаправлении в уже существующий файл?

Ответ: > полностью перезаписывает содержимое файла, удаляя всё, что было в нём раньше; >> дописывает новые данные в конец файла, сохраняя ранее существовавшее содержимое.

Вопрос 2. Что произойдёт с сообщениями об ошибках (stderr), если перенаправить в файл только stdout командой с одиночным >, без дополнительных указаний относительно stderr?

Ответ: Сообщения об ошибках продолжат выводиться на экран терминала как обычно, потому что перенаправление stdout никак не затрагивает независимый поток stderr, если он не перенаправлен отдельно (например, через 2> или конструкцию 2>&1).

Итоги урока

Вы изучили: операторы перенаправления >, >>, <, 2>, а также специальный файл /dev/null и конструкцию 2>&1 для объединения потоков.

Вы умеете: сохранять вывод команд в файлы (с перезаписью или дозаписью), избирательно управлять потоком ошибок, подавлять ненужный вывод.

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

Урок 6.3. Конвейер (pipe) `|` и цепочки команд

Освоить конвейер — механизм, который многие считают одной из самых элегантных и мощных идей во всей философии UNIX/Linux, позволяющий строить сложные цепочки обработки данных из простых, независимых друг от друга команд.

Теория

В предыдущем уроке мы научились перенаправлять вывод команды в файл. Конвейер (pipe, обозначается символом |, обычно расположенным на клавиатуре над клавишей Enter или рядом с ней, часто вместе с обратным слэшем) решает похожую, но более прямую задачу: он передаёт стандартный вывод (stdout) одной команды напрямую на стандартный ввод (stdin) другой команды, минуя промежуточное сохранение в файл на диске.

Представьте себе конвейер на заводе (отсюда, собственно, и название механизма): один рабочий обрабатывает деталь и передаёт результат следующему рабочему по ленте конвейера, тот выполняет свою операцию и передаёт результат дальше, и так далее — без необходимости складывать промежуточный результат на склад (файл) между каждым этапом.

Синтаксис невероятно прост: команда1 | команда2 — вывод команда1 становится вводом для команда2. Можно строить сколь угодно длинные цепочки: команда1 | команда2 | команда3 | команда4 — данные последовательно проходят через каждый этап обработки.

Именно конвейеры и позволяют в полной мере реализовать философию UNIX, о которой мы говорили ещё в уроке 1.1: вместо создания одной огромной, сложной программы, которая «умеет всё сразу», разработчики создают множество маленьких, простых, узкоспециализированных команд, каждая из которых прекрасно выполняет одну конкретную задачу, — а пользователь комбинирует их в конвейеры под свои конкретные нужды, получая практически безграничную гибкость из ограниченного набора простых «строительных блоков».

Важное практическое замечание: далеко не каждая команда одинаково хорошо подходит для использования в качестве второго (и далее) звена конвейера — некоторые команды не умеют читать данные из stdin вообще (они рассчитаны только на аргументы командной строки, как файлы, которые нужно обработать), в то время как другие, специально предназначенные для обработки текстовых потоков (мы разберём несколько таких в следующих уроках этого блока — grep, sort, uniq, wc, cut), созданы именно для того, чтобы органично встраиваться в середину длинных конвейерных цепочек.

Практика

Шаг 1. Выполним простой пример конвейера, объединив уже знакомую команду ls -la с новой для нас командой wc (Word Count, «подсчёт слов» — подробно разберём её в уроке 6.6, а пока используем в самом простом виде): ls -la ~ | wc -l.

Что делает эта команда: ls -la ~ выводит подробный список содержимого домашней папки; символ | передаёт этот список не на экран, а напрямую на вход команде wc -l, которая подсчитывает количество строк в полученных данных.

Что вы увидите: одно число — количество файлов и папок (плюс несколько служебных строк вывода ls -l, включая уже знакомые нам . и ..) в вашей домашней папке.

[Скриншот терминала]

Шаг 2. Построим более длинную цепочку. Введите cat ~/Linux-Course/Block-05/output.txt | wc -w (если у вас нет этого конкретного файла из предыдущего блока — используйте любой доступный текстовый файл из ваших предыдущих практических заданий, заменив путь).

Что делает эта команда: cat выводит содержимое файла, которое через конвейер передаётся команде wc -w, подсчитывающей уже не строки, а количество слов в тексте.

Шаг 3. Попробуем более практически полезный пример — найти в системном списке процессов (эту тему подробно разберём в Блоке 10, но полезно увидеть применение конвейера уже сейчас) конкретный процесс по имени, скомбинировав команду ps (покажет список работающих процессов) с уже знакомой нам по Блоку 5 идеей поиска, но теперь через команду grep (подробно разберём её в уроке 6.5): ps aux | grep bash.

Что произойдёт: ps aux выведет длинный список всех работающих процессов в системе; конвейер передаст этот список команде grep bash, которая отфильтрует и покажет только те строки, где встречается слово «bash» — вероятно, включая как минимум ваш собственный текущий процесс терминала.

[Скриншот результата]

Шаг 4. Постройте цепочку из трёх звеньев, скомбинировав уже опробованные элементы: ps aux | grep bash | wc -l.

Что произойдёт: сначала получаем список всех процессов, затем фильтруем только строки с упоминанием «bash», а затем подсчитываем, сколько именно таких строк получилось — итоговым результатом будет одно число, показывающее, сколько процессов, связанных с bash, сейчас работает в системе.

Разбор команд

Оператор конвейера |

Как определить: команда справа от | либо игнорирует переданные ей данные, либо явно жалуется на отсутствие необходимого аргумента, несмотря на то что данные вроде бы были переданы через конвейер.

Как исправить: свериться с документацией (man, урок 4.7) конкретной команды, чтобы понять, действительно ли она поддерживает чтение из stdin, и если нет — поискать альтернативный способ передачи данных (например, через специальную конструкцию подстановки команд, которую мы кратко упомянем далее в этом же блоке, в уроке 6.8).

Возможные ошибки

Почему возникает: желание сразу получить готовый, «эффектный» результат сложной обработки данных, минуя пошаговую проверку.

Как определить: финальный результат конвейера явно неверный или пустой, но неясно, на каком именно из нескольких звеньев цепочки произошла ошибка.

Как исправить: временно «разбирать» длинный конвейер на части, выполняя команды по очереди, начиная с первой, и проверяя вывод каждой команды по отдельности, прежде чем добавлять следующее звено цепочки.

Как избежать: при построении новых, ещё не проверенных конвейерных цепочек — строить их постепенно, добавляя по одному звену за раз и проверяя промежуточный результат на каждом шаге, а не писать сразу длинную цепочку из пяти-шести команд.

Практические задания

  1. Постройте конвейер, который подсчитывает количество файлов (не строк вообще, а именно файлов, без учёта самой первой служебной строки total, которую выводит ls -l — эту особенность можно пока не учитывать точно, просто понаблюдайте за результатом) в папке /etc командой вида ls -l /etc | wc -l.
  2. Используя ps aux | grep [начало имени интересующего вас процесса], найдите в списке процессов упоминания любой известной вам запущенной программы (например, «firefox», если браузер сейчас открыт).
  3. Постройте цепочку из как минимум трёх последовательных команд по своему усмотрению, используя уже изученные в этом и предыдущих блоках инструменты, и опишите в конспекте, что делает каждое звено цепочки.

Проверка знаний

Вопрос 1. Что именно передаётся через символ | от одной команды к другой?

Ответ: Стандартный вывод (stdout) команды слева от | передаётся как стандартный ввод (stdin) команде справа от |.

Вопрос 2. В чём принципиальное отличие конвейера | от перенаправления > в файл?

Ответ: Конвейер передаёт данные напрямую от одной команды к другой, минуя промежуточное сохранение на диск; перенаправление > сохраняет вывод команды в конкретный файл на диске, из которого потом можно (при необходимости, отдельной командой) снова прочитать эти данные.

Итоги урока

Вы изучили: механизм конвейера | и принцип построения цепочек команд.

Вы умеете: комбинировать простые команды в более сложные цепочки обработки данных.

В следующем уроке потребуется: понимание конвейеров, чтобы освоить конкретные команды просмотра текстовых данных, которые особенно часто используются именно в составе конвейерных цепочек.

Урок 6.4. Просмотр текста: `cat`, `less`, `more`, `head`, `tail`, `tail -f`

Освоить полный набор инструментов для просмотра текстовых файлов разного объёма и назначения — от коротких заметок до многогигабайтных системных журналов, за которыми нужно наблюдать в реальном времени.

Теория

Мы уже знакомы с cat из Блока 5 — она выводит содержимое файла целиком, сразу. Это прекрасно подходит для коротких файлов, но становится крайне неудобным для больших: весь текст «пролетит» мимо видимой области экрана терминала за долю секунды, и вы увидите только самый конец файла.

less — программа для постраничного просмотра текста (мы уже кратко сталкивались с похожим механизмом, когда просматривали man-страницы в уроке 4.7 — технически это тот же самый инструмент less, работающий «под капотом» у команды man). Название «less» — это шутливая игра слов относительно более старой аналогичной программы more (описанной ниже): «less is more» — известная английская фраза, означающая «меньшее есть большее», обыгранная как программное название («less» умеет больше, чем «more»). Внутри less работает та же самая навигация, что мы уже изучили для man-страниц: пробел — вперёд на страницу, b — назад, стрелки — построчно, /текст — поиск, q — выход.

more — более старая, исторически более ранняя программа с похожим назначением, но значительно более ограниченными возможностями по сравнению с less (например, more не позволяет прокручивать текст назад в некоторых базовых реализациях). Сегодня more используется значительно реже, в основном по историческим причинам совместимости — less практически полностью её вытеснила на современных системах, включая Ubuntu.

head (буквально «голова») — выводит только самое начало файла (по умолчанию — первые 10 строк).

tail (буквально «хвост») — выводит только самый конец файла (тоже по умолчанию последние 10 строк). Это особенно ценный инструмент при работе с журналами (Блок 10, Блок 18), где самая свежая, актуальная информация всегда находится именно в конце файла, а сам файл может быть огромным.

tail -f (follow, «следовать») — особый, чрезвычайно полезный режим команды tail, который не просто показывает последние строки файла один раз, а продолжает работать и в реальном времени показывать новые строки, по мере того как они добавляются в файл (например, кем-то другим процессом, дописывающим в него новые записи, — вспомните оператор >> из урока 6.2, именно такой механизм часто используется при ведении журналов). Это один из главных инструментов «живого» наблюдения за системой в реальном времени, особенно активно применяемый в Блоке 10 и Блоке 18 при мониторинге журналов и диагностике проблем.

Практика

Шаг 1. Создайте тестовый файл с большим количеством строк для практики — сгенерируем его с помощью цикла, слегка забегая вперёд относительно Блока 15 (не переживайте, если синтаксис цикла пока не до конца понятен, — здесь он используется просто как удобный инструмент для быстрой генерации тестовых данных, подробно циклы разберём позже): for i in $(seq 1 100); do echo "Строка номер $i"; done > big_file.txt.

Что произойдёт: будет создан файл big_file.txt со 100 пронумерованными строками.

Шаг 2. Попробуйте вывести файл целиком через cat big_file.txt и обратите внимание, что все 100 строк промелькнули на экране, но видна на экране в итоге осталась только небольшая последняя часть (в зависимости от размера окна вашего терминала).

Шаг 3. Теперь откройте тот же файл через less big_file.txt.

Что вы увидите: файл откроется в удобном полноэкранном режиме постраничного просмотра, где вы можете спокойно листать содержимое, используя уже знакомую навигацию (пробел, b, стрелки, /, q).

[Скриншот терминала]

Шаг 4. Найдите внутри less строку номер 42 с помощью поиска: нажмите /, введите номер 42, нажмите Enter. Выйдите из less клавишей q.

Шаг 5. Выведите только первые 5 строк файла: head -n 5 big_file.txt.

Что вы увидите: строки с 1 по 5.

Шаг 6. Выведите последние 5 строк: tail -n 5 big_file.txt.

Что вы увидите: строки с 96 по 100.

[Скриншот результата]

Шаг 7. Потренируем режим реального времени tail -f. В этом терминале введите tail -f big_file.txt и оставьте команду выполняться (она не завершится сама — вспомните Ctrl+C из урока 4.5 для прерывания).

Что произойдёт: терминал покажет последние строки файла и «зависнет» в ожидании новых данных, без возврата к обычному приглашению командной строки.

Шаг 8. Откройте второй, отдельный терминал (через «Activities» или Ctrl+Alt+T, как мы изучали в Блоке 4), перейдите в ту же папку (cd ~/Linux-Course/Block-06 или где вы создавали файл) и допишите в файл новую строку: echo "Новая строка добавлена только что" >> big_file.txt.

Шаг 9. Вернитесь к первому терминалу, где всё ещё выполняется tail -f.

Что вы увидите: новая строка автоматически появится в первом терминале практически мгновенно, без каких-либо дополнительных действий с вашей стороны — именно это и называется «отслеживанием файла в реальном времени», критически важная возможность для наблюдения за системными журналами.

Шаг 10. Прервите выполнение tail -f в первом терминале сочетанием Ctrl+C, чтобы вернуться к обычному приглашению командной строки.

Разбор команд

Команда less

Способы исправления: клавиша q.

Команда head

Команда tail

Способы исправления: нажать Ctrl+C, чтобы корректно завершить режим слежения и вернуться к обычному приглашению.

Возможные ошибки

Почему возникает: незнание о специализированном режиме tail -f, ориентированном именно на такую задачу.

Как определить: неудобство и трудоёмкость постоянного ручного повторения команды cat для отслеживания изменений.

Как исправить: переключиться на использование tail -f для любых задач, связанных с наблюдением за файлом, который активно дополняется новыми данными в реальном времени.

Как избежать: запомнить чёткое правило: cat/less/head — для одноразового просмотра статичного (на момент просмотра) содержимого; tail -f — специально для отслеживания живого, постоянно растущего файла.

Практические задания

  1. Создайте тестовый файл с 50 строками (аналогично шагу 1 практики этого урока, изменив число в seq) и выведите с помощью head и tail первые и последние 7 строк соответственно.
  2. Откройте любой достаточно длинный файл (например, созданный вами big_file.txt) через less, найдите через поиск (/) любую конкретную строку по номеру, и выйдите корректно клавишей q.
  3. Повторите эксперимент с tail -f из практики этого урока в двух отдельных терминалах, но на этот раз добавьте в файл не одну, а три новые строки последовательно (с небольшими паузами между ними, например используя уже знакомую команду sleep из Блока 4) и понаблюдайте, как каждая из них появляется в реальном времени в первом терминале.

Проверка знаний

Вопрос 1. Чем tail -f принципиально отличается от обычного, однократного вызова tail без параметра -f?

Ответ: Обычный tail один раз выводит последние строки файла на момент выполнения команды и сразу завершается. tail -f продолжает работать после этого, «наблюдая» за файлом и автоматически выводя новые строки по мере их добавления в файл в реальном времени, до тех пор пока выполнение не будет прервано (например, сочетанием Ctrl+C).

Вопрос 2. Почему для просмотра больших файлов предпочтительнее использовать less, а не cat?

Ответ: cat выводит весь файл сразу целиком, из-за чего большая часть содержимого «пролетает» мимо видимой области экрана без возможности удобной навигации. less открывает файл в интерактивном постраничном режиме, позволяющем спокойно листать содержимое в обоих направлениях и искать нужный текст.

Итоги урока

Вы изучили: команды less, more, head, tail и особый режим tail -f для наблюдения за файлами в реальном времени.

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

В следующем уроке потребуется: эти навыки просмотра, чтобы освоить полноценный поиск текста внутри файлов с помощью команды grep.

Урок 6.5. Поиск в тексте: `grep` и основы регулярных выражений

Освоить один из самых часто используемых в повседневной практике администратора инструментов — команду grep для поиска текста внутри файлов и потоков данных, а также познакомиться с базовыми принципами регулярных выражений, которые значительно расширяют возможности такого поиска.

Теория

grep (название происходит от исторической команды текстового редактора edg/re/p, что означало «global regular expression print», «глобальный поиск по регулярному выражению с выводом результата» — сегодня это самостоятельная, отдельная команда) — программа для поиска строк, соответствующих заданному шаблону (текстовому образцу), внутри файлов или входного потока данных (что делает её идеальным кандидатом для использования в конвейерах, как мы уже видели в уроке 6.3 на примере ps aux | grep bash).

В простейшем виде grep ищет буквальное текстовое совпадение: grep "искомый_текст" файл.txt выведет все строки файла, содержащие указанный текст. Но настоящая мощь grep раскрывается при использовании регулярных выражений (regular expressions, часто сокращают до regex или regexp) — специального языка описания шаблонов текста, значительно более гибкого, чем простое буквальное совпадение.

Регулярное выражение — это своего рода «язык шаблонов», где определённые специальные символы имеют особое значение, отличное от их буквального написания. Разберём базовые, самые часто используемые элементы:

Важное практическое различие: grep по умолчанию понимает так называемые базовые регулярные выражения (BRE, Basic Regular Expressions), где некоторые специальные символы (например, для группировки скобками) требуют дополнительного экранирования обратным слэшем. Существует также опция -E (или отдельная команда egrep), включающая поддержку расширенных регулярных выражений (ERE, Extended Regular Expressions), с более удобным и интуитивным синтаксисом без необходимости лишнего экранирования — на практике большинство администраторов предпочитают использовать именно grep -E для сколько-нибудь сложных шаблонов поиска. Более глубокое, продвинутое изучение регулярных выражений мы проведём отдельно в Блоке 15 (урок 15.8), а здесь закладываем базовое, практически достаточное понимание для повседневных задач поиска текста.

Практика

Шаг 1. Используем уже созданный в предыдущем уроке файл big_file.txt (100 пронумерованных строк). Найдём строку с конкретным числом: grep "номер 42" big_file.txt.

Что вы увидите: единственную строку «Строка номер 42» (при условии, что число 42 действительно встречается только один раз в этом сгенерированном файле).

[Скриншот терминала]

Шаг 2. Используем опцию -i (ignore case, «игнорировать регистр») для поиска без учёта заглавных и строчных букв: grep -i "СТРОКА" big_file.txt.

Что произойдёт: несмотря на то что в файле текст написан с маленькой буквы («Строка»), а вы искали заглавными («СТРОКА»), поиск с -i всё равно найдёт все совпадающие строки, игнорируя разницу в регистре.

Шаг 3. Используем опцию -c (count, «подсчитать») для подсчёта количества совпадений вместо их вывода: grep -c "Строка" big_file.txt.

Что вы увидите: число 100 — количество строк, содержащих слово «Строка» (что логично, ведь именно это слово повторяется в каждой строке нашего тестового файла).

Шаг 4. Попробуем регулярное выражение с использованием ^ для поиска строк, начинающихся определённым образом: grep "^Строка номер 1" big_file.txt.

Что вы увидите: строки «Строка номер 1», «Строка номер 10», «Строка номер 11»... и так далее до «Строка номер 19», а также «Строка номер 100» — все строки, где сразу после «Строка номер 1» следует что угодно ещё (включая отсутствие продолжения) — потому что мы не указали, что строка должна заканчиваться именно на этом месте.

[Скриншот результата]

Шаг 5. Уточним поиск с помощью $, чтобы найти строго строку номер 1, без совпадений вроде «10», «11»: grep "^Строка номер 1$" big_file.txt.

Что вы увидите: теперь только одна строка — «Строка номер 1», потому что мы явно указали, что после «1» строка должна сразу заканчиваться ($).

Шаг 6. Используем grep в составе конвейера, объединив с уже знакомой командой из Блока 5: ls -la ~ | grep "^d".

Что делает эта команда: вспомните из урока 5.4, что в выводе ls -l первый символ строки d означает, что это папка. Комбинируя ls -la с grep "^d", мы получаем список, содержащий только строки, начинающиеся с d — то есть только папки, отфильтровав из общего списка все обычные файлы.

Разбор команд

Команда grep

Как определить: неожиданное поведение команды, ошибки синтаксиса, или поиск работает не так, как ожидалось.

Способы исправления: всегда заключать искомый шаблон в кавычки (одинарные ' или двойные ") для предсказуемого и надёжного поведения, особенно при использовании специальных символов регулярных выражений.

Возможные ошибки

Почему возникает: по умолчанию grep учитывает регистр (различает заглавные и строчные буквы) строго, что не всегда интуитивно ожидаемо для новичка, привыкшего к менее строгому поиску в других контекстах (например, в некоторых поисковых системах или текстовых редакторах, которые по умолчанию игнорируют регистр).

Как определить: ожидаемое совпадение не находится, хотя текст явно присутствует в файле, но написан с иным регистром букв.

Как исправить: добавить опцию -i к уже введённой команде.

Как избежать: по умолчанию использовать -i, если нет явной, осознанной причины требовать точного совпадения регистра.

Практические задания

  1. В файле big_file.txt найдите с помощью grep строку номер 99 таким образом, чтобы результат не включал никаких других строк, случайно содержащих «99» как часть другого числа (используйте комбинацию ^ и $, как в шаге 5 практики урока).
  2. Используйте grep -v для вывода всех строк файла big_file.txt, которые НЕ содержат число «5» ни в каком виде (то есть отфильтровать 5, 15, 25, 35, 45, 50-59, 65, 75, 85, 95).
  3. Постройте конвейер, аналогичный шагу 6 практики урока, но для поиска, наоборот, только обычных файлов (не папок) в своей домашней папке, используя grep -v "^d" вместо grep "^d".

Проверка знаний

Вопрос 1. Что делает опция -v команды grep?

Ответ: Она инвертирует результат поиска — вместо строк, соответствующих шаблону, выводятся, наоборот, все строки, которые шаблону НЕ соответствуют.

Вопрос 2. Что означают специальные символы ^ и $ в регулярном выражении, и для чего полезно использовать их вместе?

Ответ: ^ обозначает начало строки, $ — конец строки. Используя оба символа вместе вокруг шаблона (например, ^текст$), можно найти строки, которые состоят строго из указанного текста целиком, а не просто содержат его где-то внутри себя вместе с другими символами.

Итоги урока

Вы изучили: команду grep и базовые принципы регулярных выражений — ., *, ^, $, [].

Вы умеете: искать текст в файлах и потоках данных, используя как простые буквальные шаблоны, так и базовые регулярные выражения.

В следующем уроке потребуется: эти навыки поиска, чтобы освоить дополнительные команды обработки текста — сортировку, удаление дубликатов, извлечение отдельных столбцов данных.

Урок 6.6. Обработка текста: `cut`, `sort`, `uniq`, `wc`

Дополнить набор инструментов обработки текста ещё четырьмя часто используемыми командами, которые вместе с уже изученными grep, head, tail образуют полноценный «набор инструментов» текстового аналитика прямо в терминале — без необходимости открывать специализированные программы вроде Excel для простых задач обработки данных.

Теория

cut — извлекает определённые части («столбцы» или «поля») из каждой строки текста, обычно разделённые каким-либо символом-разделителем (например, запятой в CSV-файлах, о которых, возможно, вы слышали в контексте электронных таблиц, или пробелом/табуляцией в выводе многих системных команд).

sort — сортирует строки текста в алфавитном (или числовом, с дополнительным параметром) порядке.

uniq (unique, «уникальный») — удаляет соседние повторяющиеся строки из текста. Важная особенность, часто вызывающая недоумение у новичков: uniq удаляет только те дубликаты, которые находятся непосредственно друг за другом, а не любые повторы по всему файлу в целом — именно поэтому uniq практически всегда используется в связке с предварительной сортировкой через sort (мы разберём это подробно в практике), которая как раз группирует одинаковые строки рядом друг с другом, делая последующую работу uniq осмысленной и корректной.

wc (Word Count, «подсчёт слов») — мы уже мельком использовали эту команду в уроке 6.3 (с параметром -l, подсчёт строк) и в уроке 6.3 (с параметром -w, подсчёт слов). Разберём её подробнее: команда подсчитывает количество строк, слов и байт (символов) в тексте.

Практика

Шаг 1. Создадим тестовый файл, имитирующий простую таблицу данных, разделённую двоеточиями (похожий формат мы позже встретим в реальном системном файле /etc/passwd в Блоке 8):

```

echo "Иван:25:Москва

Мария:30:Санкт-Петербург

Пётр:22:Москва

Анна:28:Казань" > people.txt

```

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

Шаг 2. Извлечём только имена (первое поле) с помощью cut: cut -d: -f1 people.txt.

Что делает команда: параметр -d: указывает, что разделителем полей является двоеточие; параметр -f1 указывает извлечь именно первое поле.

Что вы увидите: список из четырёх имён, без возраста и города.

[Скриншот терминала]

Шаг 3. Извлечём города (третье поле): cut -d: -f3 people.txt.

Шаг 4. Отсортируем список городов по алфавиту: cut -d: -f3 people.txt | sort.

Что вы увидите: список городов в алфавитном порядке — «Казань», «Москва», «Москва», «Санкт-Петербург» (обратите внимание, «Москва» встречается дважды подряд после сортировки, так как встречается в исходных данных дважды).

Шаг 5. Уберём дублирующиеся соседние строки с помощью uniq: cut -d: -f3 people.txt | sort | uniq.

Что вы увидите: теперь «Москва» встречается только один раз — конвейер из трёх команд сначала извлёк города, затем отсортировал их (сделав дубликаты соседними), и наконец uniq убрал повторы.

[Скриншот результата]

Шаг 6. Посчитаем, сколько раз встречается каждый уникальный город, с помощью параметра -c команды uniq: cut -d: -f3 people.txt | sort | uniq -c.

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

Шаг 7. Посчитаем общее количество строк, слов и символов в исходном файле: wc people.txt.

Что вы увидите: три числа подряд и имя файла — количество строк, слов и байт соответственно.

Разбор команд

Команда cut

Способы исправления: предварительно просмотреть исходный файл (например, через head или cat), чтобы точно определить, какой символ используется в качестве разделителя, прежде чем указывать его в параметре -d.

Команда sort

Способы исправления: добавить параметр -n при сортировке столбцов, содержащих числовые значения.

Команда uniq

Как определить: явные, видимые невооружённым глазом дубликаты остаются в результате работы uniq, вопреки ожиданиям.

Способы исправления: всегда предварительно сортировать данные (sort) перед применением uniq, если нет полной уверенности, что одинаковые строки в исходных данных и так расположены строго друг за другом.

Команда wc

Возможные ошибки

Почему возникает: непонимание того, что uniq работает только с соседними строками, о чём подробно говорилось в теории этого урока.

Как определить: дубликаты, находящиеся в разных, неcоседних местах исходных данных, остаются неучтёнными в результате.

Как исправить: переставить команды местами: сначала sort, затем uniq.

Как избежать: запомнить это правило как устойчивую пару: «sort, затем uniq» — почти всегда, когда нужен именно uniq, ему должен предшествовать sort, если нет полной уверенности в изначальной упорядоченности исходных данных.

Практические задания

  1. Используя файл people.txt из практики урока, извлеките столбец с возрастом (второе поле) и отсортируйте его по возрастанию с помощью числовой сортировки (sort -n).
  2. Создайте текстовый файл с намеренно повторяющимися строками не по соседству друг с другом (например, вручную через любой текстовый редактор или последовательные команды echo >>), продемонстрируйте на нём, что uniq без предварительного sort не удаляет такие неcоседние дубликаты, а с предварительным sort — успешно удаляет.
  3. Постройте цепочку cut -d: -f3 people.txt | sort | uniq -c | sort -rn, чтобы получить список городов, отсортированный по частоте встречаемости от самого популярного к наименее популярному — объясните в конспекте назначение каждого из четырёх звеньев этой цепочки.

Проверка знаний

Вопрос 1. Почему команду uniq почти всегда используют вместе с предварительной сортировкой sort?

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

Вопрос 2. Что произойдёт при сортировке чисел «9» и «10» командой sort без параметра -n?

Ответ: Без параметра -n сортировка происходит алфавитно (посимвольно), а не как настоящих чисел, поэтому «10» окажется «меньше» («раньше» в алфавитном порядке), чем «9», так как символ «1» предшествует символу «9» — это неверно с точки зрения обычной числовой логики, поэтому для чисел необходим параметр -n.

Итоги урока

Вы изучили: команды cut, sort, uniq, wc для извлечения полей, сортировки, удаления дубликатов и подсчёта элементов текста.

Вы умеете: строить содержательные аналитические конвейеры для обработки структурированных текстовых данных.

В следующем уроке потребуется: весь накопленный набор инструментов обработки текста, чтобы познакомиться с двумя ещё более мощными, хотя и более сложными инструментами — sed и awk.

Урок 6.7. Введение в `sed` и `awk`

Получить базовое, практически применимое знакомство с двумя мощнейшими инструментами обработки текста в Linux — sed и awk. Оба инструмента настолько обширны, что заслуживают отдельных полноценных курсов, поэтому здесь мы закладываем именно практический минимум, достаточный для решения повседневных задач системного администрирования, оставляя пространство для дальнейшего самостоятельного углубления за рамками этого курса.

Теория

sed (Stream EDitor, «потоковый редактор») — программа, которая позволяет автоматически изменять текст «на лету», без необходимости открывать файл в интерактивном текстовом редакторе (Блок 7). Самое частое практическое применение sed — операция поиска и замены текста, аналогичная функции «найти и заменить» в любом текстовом или графическом редакторе, но выполняемая прямо из командной строки, что делает её пригодной для автоматизации в скриптах (Блок 15).

Базовый синтаксис команды замены в sed выглядит так: sed 's/что_искать/на_что_заменить/', где буква s означает «substitute» («заменить»), а символ / служит разделителем между частями команды (можно использовать и другие символы-разделители при необходимости, если, например, сам искомый текст содержит слэш).

awk — это не просто команда, а целый компактный язык программирования, специально созданный для обработки текста, организованного в виде строк и столбцов (полей) — концептуально похоже на уже знакомую нам команду cut из предыдущего урока, но значительно мощнее и гибче, с поддержкой условий, вычислений и других элементов полноценного программирования. Название происходит от фамилий трёх его создателей — Aho, Weinberger, Kernighan.

В простейшем виде awk автоматически разбивает каждую строку входных данных на поля по указанному разделителю (по умолчанию — любой пробельный символ) и позволяет обращаться к каждому полю по номеру через специальные переменные $1, $2, $3 и так далее (обратите внимание: это тот же самый принцип обозначения, что мы видели у аргументов Bash-скриптов, к которым подробно вернёмся в Блоке 15) — а переменная $0 при этом означает всю строку целиком.

Оба инструмента, sed и awk, обладают несравнимо более широкими возможностями, чем показано в этом ознакомительном уроке — здесь заложена лишь практическая база, к которой мы ещё вернёмся и слегка расширим её в Блоке 15, при написании реальных скриптов автоматизации.

Практика

Шаг 1. Используем уже знакомый файл people.txt из предыдущего урока. Заменим слово «Москва» на «Moscow» с помощью sed: sed 's/Москва/Moscow/' people.txt.

Что произойдёт: команда выведет содержимое файла с произведённой заменой — но обратите внимание, сам исходный файл при этом останется неизменным (проверьте это командой cat people.txt после выполнения — вы увидите, что там по-прежнему «Москва», а не «Moscow»), так как по умолчанию sed только выводит изменённый результат в stdout, не трогая исходный файл.

[Скриншот терминала]

Шаг 2. Если действительно требуется изменить сам файл «на месте», используется параметр -i (in-place, «на месте»): sed -i 's/Москва/Moscow/' people.txt.

Что произойдёт: на этот раз файл будет изменён физически. Проверьте результат командой cat people.txt — теперь вы должны увидеть «Moscow» вместо «Москва».

Важное предупреждение: параметр -i изменяет файл безвозвратно, без возможности отмены стандартными средствами (аналогично уже изученным нами предупреждениям об осторожности с rm в Блоке 5) — рекомендуется на первых порах обучения тестировать команды sed без -i, проверяя корректность результата на экране, и добавлять -i только тогда, когда вы полностью уверены в правильности команды.

Шаг 3. Используем awk для извлечения второго поля (по аналогии с cut из предыдущего урока, но теперь применительно к результату другой команды через конвейер) — например, извлечём второй столбец из вывода команды ls -l, который соответствует количеству жёстких ссылок (вспомните уроки 5.4 и 5.7): ls -l ~ | awk '{print $2}'.

Что произойдёт: awk автоматически разобьёт каждую строку вывода ls -l на поля по пробелам и выведет только второе поле каждой строки.

[Скриншот результата]

Шаг 4. Используем awk с указанием собственного разделителя полей (аналогично параметру -d команды cut), применительно к нашему файлу people.txt с разделителем-двоеточием: awk -F: '{print $1, $3}' people.txt.

Что делает эта команда: параметр -F: указывает, что разделителем полей является двоеточие; конструкция {print $1, $3} означает «вывести первое и третье поле» (имя и город, пропустив возраст).

Что вы увидите: список пар «имя город» для каждой строки исходного файла.

Разбор команд

Команда sed

Способы исправления: добавить флаг g в конец выражения замены.

Команда awk

Возможные ошибки

Почему возникает: спешка, недостаточная осторожность при работе с потенциально необратимой командой.

Как определить: конфигурационный файл или другой важный документ содержит неожиданные, ошибочные изменения.

Как исправить: восстановление из резервной копии (Блок 11) — это ещё одно практическое напоминание о критической важности регулярного резервного копирования, особенно перед выполнением потенциально необратимых операций редактирования системных файлов.

Как избежать: всегда сначала выполнять команду sed без параметра -i, визуально проверяя корректность результата на экране, и добавлять -i только после полной уверенности в правильности команды; для особо важных файлов дополнительно создавать ручную резервную копию перед изменением (например, простым cp important.conf important.conf.backup, используя уже знакомую нам команду из Блока 5).

Практические задания

  1. Используя sed (без -i, только для просмотра результата), замените в файле people.txt слово «Иван» на любое другое имя по вашему выбору, и убедитесь, что исходный файл остался неизменным после выполнения команды.
  2. Создайте резервную копию файла people.txt (командой cp, как рекомендовано в разделе «Возможные ошибки»), а затем выполните ту же замену уже с параметром -i, применив изменения непосредственно к файлу.
  3. Используя awk, выведите только имена людей старше 25 лет из файла people.txt — для этого потребуется использовать условное выражение внутри awk вида awk -F: '$2 > 25 {print $1}' people.txt; попробуйте разобраться в этой конструкции самостоятельно и объяснить в конспекте своими словами, что делает каждая её часть, опираясь на уже изученный в этом уроке материал.

Проверка знаний

Вопрос 1. Что произойдёт с исходным файлом, если выполнить команду sed 's/старое/новое/' file.txt без параметра -i?

Ответ: Ничего не произойдёт — исходный файл останется полностью неизменным. Команда без -i только выводит результат замены в стандартный вывод (на экран), не затрагивая физическое содержимое файла на диске.

Вопрос 2. С какого номера начинается нумерация полей в командах awk, и что означает специальная переменная $0?

Ответ: Нумерация полей начинается с 1 (то есть $1 — первое поле, $2 — второе, и так далее). Переменная $0 — особая, зарезервированная переменная, обозначающая всю строку целиком, а не какое-либо отдельное поле.

Итоги урока

Вы изучили: базовые возможности sed (поиск и замена текста) и awk (обработка текста по полям, включая простые условия).

Вы умеете: выполнять автоматизированную замену текста в файлах и извлекать/фильтровать данные по столбцам с условиями.

В следующем уроке потребуется: накопленное владение обработкой текста, чтобы освоить переменные окружения и настройку удобных сокращений команд.

Урок 6.8. Переменные окружения, `export`, `$PATH`, `alias`

Понять механизм переменных окружения — важную концепцию, которая уже несколько раз мелькала в предыдущих уроках этого блока ($SHELL в уроке 4.2, $HOME в уроке 5.3, $? в уроке 4.6) — а также освоить создание удобных сокращений для часто используемых команд.

Теория

Переменная окружения (environment variable) — это именованное значение, хранящееся в памяти текущей сессии терминала (и, при определённых условиях, передаваемое дочерним процессам, запускаемым из этой сессии), к которому можно обращаться по имени в любой момент работы. Мы уже несколько раз использовали такие переменные, не разбирая их подробно: $SHELL (путь к текущей оболочке), $HOME (путь к домашней папке), $PATH (список папок для поиска команд), $? (код завершения последней команды).

Синтаксис обращения к значению переменной — знак доллара $ перед её именем, как мы уже видели во всех перечисленных примерах. Чтобы создать собственную новую переменную (или изменить значение существующей) в текущей сессии Bash, используется простой синтаксис присваивания без пробелов вокруг знака равенства: ИМЯ_ПЕРЕМЕННОЙ=значение.

Важное различие между обычной переменной оболочки (shell variable) и переменной окружения (environment variable) в строгом смысле: обычная переменная, созданная простым присваиванием (ИМЯ=значение), доступна только в пределах текущей сессии Bash и не передаётся автоматически другим программам, которые вы запускаете из этой сессии. Чтобы сделать переменную по-настоящему переменной окружения, доступной и для запускаемых из этой сессии дочерних программ, используется команда export: export ИМЯ_ПЕРЕМЕННОЙ=значение.

Особенно важная и часто используемая переменная — $PATH. Мы уже упоминали её в уроке 5.8 применительно к команде which: это список папок (путей), разделённых двоеточием, в которых Bash ищет исполняемые файлы команд, когда вы вводите имя команды в терминале. Именно благодаря $PATH вы можете набрать просто ls, а не полный путь /usr/bin/ls каждый раз — Bash автоматически проверяет все папки, перечисленные в $PATH, по очереди, пока не найдёт программу с таким именем.

alias (псевдоним) — механизм, позволяющий создать собственное короткое имя-сокращение для длинной или часто повторяемой команды. Это чисто удобство, не влияющее на саму систему, но значительно ускоряющее повседневную работу опытных администраторов, которые обычно накапливают целый набор личных удобных сокращений за время работы.

Важное практическое замечание, связывающее эту тему с уже изученным материалом Блока 5: и переменные, созданные через export, и alias, действуют только в рамках текущей сессии терминала и исчезают при её закрытии, если не сохранены в специальном конфигурационном файле — а именно в уже знакомом нам скрытом файле ~/.bashrc (вспомните урок 5.9), который автоматически выполняется при запуске каждой новой сессии Bash. Полноценно мы поработаем с редактированием этого файла уже в Блоке 7, когда освоим текстовые редакторы терминала.

Практика

Шаг 1. Посмотрите текущее значение переменной $PATH: echo $PATH.

Что вы увидите: длинную строку из нескольких путей, разделённых двоеточиями, например /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin (конкретный список может немного отличаться) — это как раз те самые папки, включая уже знакомые нам по уроку 5.2 /bin и /usr/bin, где система ищет исполняемые команды.

[Скриншот терминала]

Шаг 2. Создайте простую переменную оболочки (без export): MY_NAME="Студент курса Linux".

Шаг 3. Выведите её значение: echo $MY_NAME.

Что вы увидите: заданный текст — переменная успешно работает в пределах текущей сессии.

Шаг 4. Создайте новую переменную с использованием export: export MY_COURSE="Linux от нуля до администратора".

Шаг 5. Выведите её значение аналогичным образом: echo $MY_COURSE.

Шаг 6. Создайте удобный alias для часто используемой команды. Например, сократим уже знакомую нам комбинацию ls -lah из Блока 5 до простого ll: alias ll='ls -lah'.

Шаг 7. Проверьте, что alias работает: просто введите ll и нажмите Enter.

Что произойдёт: будет выполнена полная команда ls -lah, хотя вы ввели лишь короткое сокращение ll.

[Скриншот результата]

Шаг 8. Создайте ещё один полезный alias, сокращающий команду для быстрого перехода в вашу учебную папку курса (подставьте свой реальный путь): alias course='cd ~/Linux-Course'.

Шаг 9. Проверьте список всех текущих активных псевдонимов командой alias без аргументов.

Что вы увидите: список всех alias, созданных в текущей сессии, включая только что созданные вами ll и course, а также, возможно, несколько alias, уже настроенных по умолчанию в Ubuntu.

Разбор команд

Команда export

Способы исправления: убрать пробелы вокруг знака равенства.

Команда alias

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

Возможные ошибки

Почему возникает: непонимание, что переменные и alias, созданные напрямую в терминале, существуют только в оперативной памяти текущей сессии и исчезают при её завершении, если не сохранены отдельно в конфигурационном файле.

Как определить: после открытия нового окна терминала ранее созданные alias или переменные оказываются недоступны (command not found при попытке использовать alias, пустое значение при попытке вывести переменную).

Как исправить: повторно создать нужные переменные/alias в новой сессии, либо (правильное долгосрочное решение) добавить соответствующие команды в файл ~/.bashrc, чтобы они автоматически создавались при запуске каждой новой сессии — подробно этот процесс редактирования разберём в Блоке 7.

Как избежать: для действительно полезных, регулярно используемых переменных и alias с самого начала планировать их сохранение в ~/.bashrc, а не полагаться на создание вручную в каждой новой сессии.

Практические задания

  1. Создайте собственный alias для любой часто используемой вами команды из предыдущих блоков курса (например, для просмотра списка процессов, связанных с bash, из урока 6.3) и проверьте его работу.
  2. Выведите значение переменной $HOME и сравните её с результатом уже знакомой нам команды echo ~ из урока 5.3 — убедитесь, что они дают одинаковый результат, и объясните в конспекте, почему так происходит.
  3. Создайте новую переменную окружения с помощью export, откройте второй, отдельный терминал (как мы делали в уроке 6.4) и проверьте, доступна ли эта переменная в новой, отдельной сессии — запишите результат в конспект и объясните его, опираясь на теорию этого урока.

Проверка знаний

Вопрос 1. В чём разница между обычной переменной оболочки и переменной окружения, созданной через export?

Ответ: Обычная переменная оболочки доступна только в текущей сессии Bash и не передаётся дочерним процессам, запускаемым из неё. Переменная окружения, созданная через export, становится доступна и дочерним процессам, унаследовавшим окружение от текущей сессии.

Вопрос 2. Что произойдёт с созданными в терминале alias и переменными после закрытия окна терминала, если они не были сохранены в файле ~/.bashrc?

Ответ: Они будут полностью утеряны — существовали только в оперативной памяти текущей сессии терминала. Для сохранения между сессиями их нужно явно добавить в конфигурационный файл ~/.bashrc, который автоматически выполняется при старте каждой новой сессии Bash.

Итоги урока

Вы изучили: понятие переменных окружения, команду export, важнейшую переменную $PATH, и механизм создания сокращений команд через alias.

Вы умеете: создавать собственные переменные и удобные сокращения команд, понимаете разницу между переменными оболочки и переменными окружения.

В следующем уроке потребуется: весь накопленный материал блока, чтобы освоить последнюю важную тему — работу с архивами и сжатием файлов.

Урок 6.9. Архивы и сжатие: `tar`, `gzip`, `zip/unzip`

Освоить создание и распаковку архивов — необходимый практический навык для резервного копирования (тему которого подробно продолжим в Блоке 11), передачи множества файлов единым пакетом, и распаковки программного обеспечения, распространяемого в архивированном виде.

Теория

Архивирование — это объединение нескольких отдельных файлов и папок в единый файл-контейнер, удобный для хранения или передачи как одно целое. Сжатие (компрессия) — это отдельный, хотя часто сопутствующий процесс, при котором данные внутри архива дополнительно уменьшаются в размере за счёт специальных алгоритмов, устраняющих избыточность в данных. Важно понимать: это два концептуально разных процесса, хотя на практике они часто выполняются одновременно, в рамках одной команды.

tar (Tape ARchive — название сохранилось с давних времён, когда подобные архивы записывались на магнитные ленты, tape) — исторически главный и самый распространённый инструмент архивирования в мире Linux. Важная особенность: сама команда tar изначально занимается именно объединением файлов в единый архив (без сжатия), а для сжатия она обычно вызывается в сочетании с отдельными алгоритмами сжатия — чаще всего gzip, что и создаёт всем известный формат .tar.gz (иногда сокращаемый до .tgz).

gzip — отдельная, самостоятельная программа сжатия, которая может использоваться и независимо от tar (для сжатия одного отдельного файла), но чаще всего в мире Linux встречается именно в сочетании с tar для создания сжатых архивов из множества файлов.

zip/unzip — формат и утилиты, значительно более распространённые в мире Windows/macOS, но полностью поддерживаемые и в Linux. В отличие от tar, где архивирование и сжатие — концептуально разделённые шаги, формат .zip сразу объединяет оба процесса в одном формате файла. Zip-архивы особенно удобны, когда файлы предполагается передавать пользователям других операционных систем, где формат .zip наиболее привычен и поддерживается «из коробки» без дополнительных программ.

Практика

Шаг 1. Перейдите в свою домашнюю папку и создайте тестовую структуру для архивирования, если у вас ещё нет подходящей папки с несколькими файлами из предыдущих блоков — можно использовать уже существующую ~/Linux-Course со всем накопленным содержимым предыдущих уроков.

Шаг 2. Создайте сжатый архив всей папки с материалами курса: tar -czvf linux-course-backup.tar.gz ~/Linux-Course.

Что делает эта команда: параметр -c (create, «создать») указывает на создание нового архива; -z указывает использовать сжатие gzip; -v (verbose, «подробно», уже знакомый нам параметр по аналогии с cp -v и mv -v из Блока 5) выводит список каждого добавляемого в архив файла в процессе работы; -f (file, «файл») указывает, что сразу следующий аргумент — это имя создаваемого файла архива.

Что произойдёт: будет создан единый сжатый файл linux-course-backup.tar.gz, содержащий внутри себя всю структуру папки ~/Linux-Course со всем её содержимым.

[Скриншот терминала]

Шаг 3. Проверьте размер получившегося архива и сравните с исходным размером папки: ls -lh linux-course-backup.tar.gz (вспомните параметр -h из Блока 5 для удобочитаемого размера).

Шаг 4. Просмотрите содержимое архива, не распаковывая его физически, с помощью параметра -t (list, «показать список»): tar -tzvf linux-course-backup.tar.gz.

Что вы увидите: полный список всех файлов и папок, находящихся внутри архива, без фактического извлечения их на диск — удобный способ проверить содержимое архива перед распаковкой, особенно полезный для больших, незнакомых архивов, полученных откуда-то извне.

Шаг 5. Создайте отдельную тестовую папку для распаковки, чтобы не перепутать с оригинальными файлами: mkdir ~/extracted_test и перейдите в неё: cd ~/extracted_test.

Шаг 6. Распакуйте архив, используя параметр -x (extract, «извлечь») вместо -c: tar -xzvf ~/linux-course-backup.tar.gz.

Что произойдёт: содержимое архива будет извлечено в текущую папку (~/extracted_test), с сохранением исходной структуры вложенных папок.

[Скриншот результата]

Шаг 7. Опробуем формат zip для сравнения (если команда zip не установлена, система сообщит об этом — установка дополнительных программ подробно разберём в Блоке 9): zip -r linux-course-backup.zip ~/Linux-Course.

Что делает параметр -r: аналогично уже знакомым нам по Блоку 5 командам, указывает на рекурсивное включение всех вложенных папок и файлов, а не только верхнего уровня.

Шаг 8. Распакуйте zip-архив в отдельную тестовую папку: mkdir ~/extracted_zip_test && cd ~/extracted_zip_test && unzip ~/linux-course-backup.zip.

Разбор команд

Команда tar

Как определить: сообщение об ошибке вида «not in gzip format» или похожее, указывающее на несоответствие ожидаемого и фактического формата данных.

Способы исправления: проверить расширение файла архива (.tar.gz/.tgz требует -z, простой .tar без сжатия — параметр -z не нужен) и подобрать соответствующий набор параметров.

Команда zip/unzip

Способы исправления: пересоздать архив с добавлением параметра -r.

Возможные ошибки

Почему возникает: спешка, недостаточная осторожность при работе с чужими архивами.

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

Как исправить: восстановление изменённых/утраченных файлов возможно только из резервной копии (Блок 11).

Как избежать: всегда сначала просматривать содержимое архива командой tar -tzvf (или для zip — unzip -l имя_архива.zip, аналогичная по смыслу опция) перед фактической распаковкой, и, как мы практиковали в этом уроке, распаковывать незнакомые архивы в отдельную, специально созданную тестовую папку, а не напрямую поверх существующих важных данных.

Практические задания

  1. Создайте сжатый .tar.gz архив любой папки из ваших материалов курса, просмотрите его содержимое командой с параметром -t без распаковки, а затем распакуйте в отдельную тестовую папку и сравните результат с оригиналом.
  2. Сравните итоговый размер файлов .tar.gz и .zip, созданных из одной и той же исходной папки (используя ls -lh для обоих), и запишите в конспект наблюдение — какой формат оказался компактнее в вашем конкретном случае (обратите внимание: конкретный результат может отличаться в зависимости от типа сжимаемых данных, это нормально и не означает, что один формат всегда лучше другого).
  3. Удалите (аккуратно, командой rm, с осторожностью, о которой мы говорили в Блоке 5) все тестовые архивы и распакованные тестовые папки, созданные в рамках практики этого урока, чтобы не загромождать домашнюю папку — это хорошая практика поддержания порядка после экспериментов.

Проверка знаний

Вопрос 1. Чем архивирование концептуально отличается от сжатия, даже если на практике они часто выполняются одной командой?

Ответ: Архивирование — это объединение нескольких отдельных файлов и папок в единый файл-контейнер. Сжатие — это отдельный процесс уменьшения размера данных за счёт устранения избыточности. Команда tar изначально занимается именно архивированием, а сжатие (обычно через gzip, параметр -z) добавляется как отдельный, хотя часто сопутствующий шаг.

Вопрос 2. Как можно просмотреть содержимое .tar.gz архива, не распаковывая его физически на диск?

Ответ: С помощью параметра -t (list) команды tar, например tar -tzvf archive.tar.gz — эта команда покажет полный список файлов внутри архива без фактического извлечения.

Итоги урока

Вы изучили: понятия архивирования и сжатия, команды tar (с параметрами -c, -x, -t, -z, -v, -f) и zip/unzip.

Вы умеете: создавать, просматривать и безопасно распаковывать архивы в форматах .tar.gz и .zip.

В следующем уроке (это будет уже Блок 7) потребуется: весь накопленный в этом блоке инструментарий обработки текста и файлов, чтобы наконец освоить полноценные текстовые редакторы терминала — nano и vim — для непосредственного редактирования конфигурационных файлов.

Мини-проект блока

Задача: смоделировать реальную задачу системного администратора — проанализировать файл журнала с помощью цепочки команд обработки текста, изученных в этом блоке.

Что нужно сделать:

  1. Создайте тестовый файл журнала access.log, имитирующий записи веб-сервера, командой, аналогичной генерации big_file.txt из урока 6.4, но с более содержательными строками — например, сгенерируйте 200 строк вида «IP-адрес - дата - код ответа», где IP-адреса и коды ответа (200, 404, 500) варьируются (это можно сделать вручную через echo >>, скопировав и слегка изменив несколько шаблонных строк несколько раз — не обязательно писать сложный генератор, важна возможность применить к результату дальнейшую обработку).
  2. С помощью grep найдите все строки с кодом ответа «404» (означающим «страница не найдена» — распространённый код ошибки веб-сервера, тему которого продолжим в Блоке 19).
  3. С помощью связки cut/awk, sort и uniq -c определите, какой IP-адрес встречается в журнале чаще всего.
  4. Сохраните результат всего анализа (пункты 2 и 3) в отдельный файл-отчёт report.txt, используя перенаправление >/>>.
  5. Заархивируйте исходный файл журнала и получившийся отчёт вместе в единый .tar.gz архив.
  6. Составьте в конспекте итоговый отчёт: какие именно команды и в каком порядке вы использовали для каждого шага, и что означает результат вашего анализа.

Критерий готовности: все шаги выполнены с использованием исключительно команд терминала, изученных в этом блоке (без открытия файла в графическом редакторе для ручного анализа), итоговый отчёт и архив созданы.

Контрольные вопросы

  1. Объясните разницу между потоками stdout и stderr и приведите пример ситуации, где важно перенаправить их по отдельности.
  2. В чём разница между > и >>? Приведите пример, где ошибочное использование одного вместо другого может привести к потере данных.
  3. Объясните принцип работы конвейера | на примере команды ps aux | grep bash | wc -l.
  4. Почему uniq почти всегда используется вместе с предварительной сортировкой sort?
  5. В чём разница между обычной переменной оболочки и переменной окружения, созданной через export?
  6. Опишите, чем принципиально отличается роль команды tar от роли gzip при создании файла с расширением .tar.gz.

Выводы по блоку

В этом блоке вы освоили один из самых мощных практических инструментариев Linux — комбинирование простых команд в сложные конвейеры обработки данных. Вы научились управлять потоками ввода/вывода/ошибок, сохранять и дописывать данные в файлы, строить многозвенные цепочки команд, искать текст с помощью регулярных выражений, сортировать и агрегировать данные, выполнять автоматическую замену текста, работать с переменными и создавать удобные сокращения команд, а также архивировать и сжимать файлы. Эти инструменты станут фундаментом для Блока 15, где вы будете писать полноценные Bash-скрипты, автоматизирующие рутинные административные задачи.

Список изученных тем

Рекомендации перед переходом

Прежде чем двигаться дальше, убедитесь, что вы можете самостоятельно построить конвейер из трёх-четырёх команд для решения конкретной практической задачи обработки текста, уверенно перенаправляете вывод и ошибки в разные файлы, и понимаете разницу между sort+uniq и произвольным неупорядоченным поиском дубликатов. Этот блок был насыщенным и техническим — не спешите переходить дальше, если чувствуете неуверенность в построении конвейеров: вернитесь и повторите практические примеры уроков 6.3, 6.5 и 6.6, где закладывается основа для всей дальнейшей работы с текстом в Linux.