3.7 Поиск файлов и текста (find, grep, wc)

В этом параграфе мы научимся находить файлы и информацию внутри них.

Мы разберём, как использовать find для поиска по имени и типу файла, как применять grep для поиска текста, чем шаблоны glob отличаются от регулярных выражений и как с помощью wc подсчитывать строки, слова и символы.

Эти приёмы помогут быстро ориентироваться в системе и анализировать большие объёмы данных.

Ключевые вопросы параграфа

  1. Как искать файлы по имени, типу или шаблону?
  2. Как искать текст внутри файлов?
  3. Что такое регулярные выражения и чем они отличаются от шаблонов glob?
  4. Как подсчитать количество строк, слов и символов?
  5. Как сравнить два файла или даже два каталога?

Поиск файлов с помощью find

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

Но что, если мы не знаем точное имя файла, а их десятки или сотни в каталоге? Мы можем получить вывод каталога командой ls, но найти конкретный файл в большом списке всё ещё будет задачей на внимательность. На помощь нам придёт команда find.

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

Базовый формат команды прост:

1find <путь> <условия> [действие]

И самый простой пример — поиск всех файлов с именем report.txt в текущем каталоге — будет выглядеть так:

1alice@ubuntu:~$ find . -name "report.txt"
2./Documents/report.txt

Разберём переданные опции:

  • . — текущий каталог (начальная точка поиска);
  • -name — условие поиска по имени;
  • "report.txt" — точное имя файла.

Мы указали имя файла и текущий каталог для поиска и нашли совпадение в каталоге ./Documents.

Но что, если нужно искать не конкретное имя? Мы можем использовать шаблоны glob, например *:

1alice@ubuntu:~$ find . -name "*.log"
2./Documents/app.log
3./Backup/test-1.log
4./Backup/test-2.log
5./hello.log

Данной командой мы найдём все файлы, имя которых оканчивается на .log в текущем каталоге (и даже в его подкаталогах).

Поиск по типу и другим свойствам

Поиск по имени — это лишь один из сценариев. find умеет находить объекты по их типу, времени изменения, размеру и другим параметрам.

Поиск по типу

1alice@ubuntu:~$ find . -type d    # найти все каталоги
2alice@ubuntu:~$ find . -type f    # найти все обычные файлы
3alice@ubuntu:~$ find . -type l    # найти все символьные ссылки

Поиск по дате изменения (mtime)

1alice@ubuntu:~$ find . -mtime -1   # изменённые за последние сутки
2alice@ubuntu:~$ find . -mtime +7   # старше недели

У mtime интересный формат: цифры -1 и 7 здесь означают, сколько полных 24-часовых периодов прошло с момента последнего изменения файла.

Здесь знак перед числом тоже имеет своё значение:

  • -N — меньше N дней,
  • N — ровно N дней,
  • +N — больше N дней.

Формат

Что означает

Пример

-mtime 0

Изменён в течение последних 24 часов

Файлы, изменённые сегодня

-mtime -1

Изменён менее чем 1 день назад

Файлы, изменённые за последние 24 часа

-mtime 1

Изменён от 24 до 48 часов назад

Файлы, изменённые вчера

-mtime +7

Изменён более чем 7 дней назад

Файлы старше недели

Помимо времени последнего изменения (mtime), find может искать по времени последнего чтения файла (atime) и времени изменения метаданных (ctime).

Тут советуем сделать паузу и вспомнить, как смотреть временные метки файла при помощи команды stat.

Поиск по размеру файла

1alice@ubuntu:~$ find . -size +100M # файлы больше 100 МБ

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

  • -100M — меньше 100 МБ,
  • 100M — ровно 100 МБ,
  • +100M — больше 100 МБ.

Помимо мегабайтов (M), мы можем использовать и другие единицы:

Суффикс

Единица

Пример

Что означает

c

Байты

-size +100c

Больше 100 Б

k

Килобайты (1024 Б)

-size -10k

Меньше 10 КБ

M

Мегабайты (1024² Б)

-size +100M

Больше 100 МБ

G

Гигабайты (1024³ Б)

-size +1G

Больше 1 ГБ

b

Блоки по 512 Б (по умолчанию)

-size 4

Ровно 4 блока (2 КБ)

Поиск по размеру и времени часто используют администраторы для анализа логов и очистки системы от временных файлов.

1alice@ubuntu:~$ find /tmp -size +1M -mtime +3 -delete

Эта команда найдёт временные файлы старше трёх дней и больше 1 МБ в каталоге /tmp и сразу же удалит встроенным действием -delete. Об этом — дальше.

Выполнение действий над найденными файлами

find умеет не только находить, но и делать что-то с результатами.
Давайте рассмотрим подробнее, какие действия нам доступны.

Удаление

-delete удаляет найденные объекты встроенными силами find — быстро и «тихо».
Подходит для файлов и пустых каталогов.

1alice@ubuntu:~$ find . -type f -name '*.tmp' -mtime +7 -delete

Важно: фильтры (-type, -name, -mtime, -size и т. п.) нужно ставить до -delete.

-delete очень удобен и не требует дополнительных усилий для написания команды удаления, но имеет ряд ограничений. Например, -delete не удаляет непустые каталоги.

Запуск команды над файлом

За это отвечают опции -exec и -ok. Разберём их по очереди.

exec может запускать команду над каждым найденным файлом (а точнее — путём), в том числе и команду rm для удаления.

У -exec есть две формы, про которые стоит знать:

  • -exec <команда> {} \; — запуск по одному пути за раз,
  • -exec <команда> {} + — запуск реже, сразу пачкой путей (обычно быстрее),
    где {} — место подстановки найденного пути.

Давайте разберём, в чём разница.
Допустим, в текущем каталоге есть несколько логов:

1alice@ubuntu:~$ ls
2app1.log  app2.log  app3.log

Давайте обработаем их при помощи формы -exec <команда> {} \;, по одному файлу за раз. Команда будет запущена столько раз, сколько файлов было найдено:

1alice@ubuntu:~$ find . -name '*.log' -exec echo "Обработка:" {} \;
2Обработка: ./app1.log
3Обработка: ./app2.log
4Обработка: ./app3.log

Для тестового прогона find -exec можно безопасно использовать команду echo.

Обратите внимание: echo в итоге вызвался три раза, по одному на каждый найденный .log-файл.
Эта форма работает медленнее, но зато поддерживает работу с командами, которые не умеют принимать несколько аргументов.

Теперь обработаем их через форму -exec <команда> {} +. Все найденные пути будут переданы команде одним списком — команда в итоге будет вызываться реже:

1alice@ubuntu:~$ find . -name '*.log' -exec echo "Обработка:" {} +
2Обработка: ./app1.log ./app2.log ./app3.log

echo вызвался только один раз, но сразу с тремя аргументами.

Такой способ быстрее и лучше подходит для команд вроде rm, gzip, tar, ls.

Зафиксируем разницу:

Форма

Что делает

Когда использовать

-exec {} \;

Запускает команду для каждого файла

Когда команда не умеет принимать несколько аргументов

-exec {} +

Запускает команду один раз для списка

Когда команда принимает несколько путей (быстрее)

Теперь поговорим об -ok. На самом деле -ok делает то же, что и exec, но требует подтверждения перед каждым запуском.

Давайте посмотрим, как это выглядит, на нашем наборе логов:

1alice@ubuntu:~$ find . -name '*.log' -ok rm -- {} \;
2< rm ... ./app1.log > ? y
3< rm ... ./app2.log > ? n
4< rm ... ./app3.log > ? y

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

  • find спрашивает перед каждым файлом — y (yes) или n (no);
  • команда rm выполняется только для тех файлов, где вы ответили y.

Важно: -ok не поддерживает форму с +, он всегда работает над одним файлом за раз, как -exec <команда> {} \;.

-ok специально сделан как интерактивный вариант -exec, и его смысл в том, чтобы задать пользователю вопрос для каждого найденного файла.
Мы можем использовать действие -ok для проверки, что всё отработало как надо. А после можно заменить на -exec «с плюсом» для ускорения и автоматизации.

Ограничение длины командной строки

Вы могли заметить и задать закономерный вопрос: а что будет, если мы запустим команду find . -name '*.log' -exec echo "Обработка:" {} + и найдём большое количество файлов? Как все эти файлы подставятся в качестве аргументов? Есть ли ограничение по длине или количеству аргументов?

Да, у системы действительно есть ограничение по общей длине аргументов при запуске процесса.
Это ограничение задаётся ядром Linux, и его можно посмотреть так:

1alice@ubuntu:~$ getconf ARG_MAX
22097152

В большинстве современных систем значение около 2 MБ (или 2097152 байта, как показано выше).
Это включает не только имена файлов, но и переменные окружения, пробелы и прочее.

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

Форма -exec <команда> {} + безопасна в использовании, даже если найденных файлов миллионы. find гарантирует, что команда не превысит системный лимит аргументов.

Примеры использования

Пример 1. Если нам нужно удалить временные .tmp-файлы старше 7 дней, мы можем просто использовать опцию -delete:

1alice@ubuntu:~$ find . -type f -name '*.tmp' -mtime +7 -delete

Пример 2. Если нам нужно удалить все каталоги cache и сделать это без подтверждений — мы можем воспользоваться опцией -exec:

1alice@ubuntu:~$ find . -type d -name 'cache' -exec rm -rf {} +

Действие -delete не позволило бы нам такое удаление из-за наличия непустых каталогов.

Пример 3. Если мы хотим получать подтверждение для каждого найденного объекта, мы можем использовать опцию -ok:

1alice@ubuntu:~$ find . -type f -name "*.bak" -ok rm {} \;

Давайте проверим, как вы разобрались с поиском файлов:

Чтобы добавить в заметки выделенный текст, нажмите Ctrl + E
Проверь себяСоедините порядковый номер со словом

Поиск текста в файлах с помощью grep

Давайте перейдём от поиска файлов к поиску внутри файлов.

Мы можем искать не только файлы по их именам, но и строки в тексте конкретного файла. Поможет нам в этом команда grep, с которой вы познакомились в предыдущем параграфе.

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

Базовый формат команды выглядит так:

1grep [опции] "шаблон" файл

И простейший пример поиска строк с ошибками в логе:

1alice@ubuntu:~$ grep "error" app.log
2Error: Hello this is error!

В результате выполнения grep мы получим вывод строк, в которых встречалось слово error.

По умолчанию поиск чувствителен к регистру, то есть Error, ERROR, и error — абсолютно разные слова с точки зрения grep.

Чтобы игнорировать регистр, используйте -i:

1alice@ubuntu:~$ grep -i "error" app.log
2Error: Hello this is error!
3ERROR: And this is another ERROR.
4Error: And another

Теперь мы получили все строки с error, независимо от регистра.

Опции grep

Помимо -i, есть и другие полезные опции:

Опция

Что позволяет делать

Пример

-i

Игнорировать регистр

grep -i "error" app.log

-n

Показать номера строк

grep -n "fail" server.log

-r

Искать рекурсивно в каталогах

grep -r "TODO" .

-v

Исключить строки с шаблоном

grep -v "INFO" log.txt

-E

Использовать расширенные регулярные выражения (подробнее далее)

grep -E "(ERROR FAIL)" log.txt

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

1alice@ubuntu:~$ grep -in "error" app.log
21:Error: Hello this is error!
33:ERROR: And this is another ERROR.
412:Error: And another

Обратите внимание: теперь мы знаем, что совпадения для error есть в строках 1, 3 и 12.

Также мы можем исключить строки по шаблону -v, например, чтобы оставить все строки, кроме содержащих ERROR:

1alice@ubuntu:~$ grep -inv "error" app.log
22:INFO: Application started
34:WARNING: Low disk space
45:INFO: Retrying connection
56:WARNING: High memory usage detected
67:INFO: Application running normally

Использование grep

grep часто применяют с пайпами (|) для фильтрации вывода других программ.
Например, команда ps aux, которая выводит информацию о запущенных процессах, выводит достаточно большой список строк (мы даже передавали его в less в предыдущем параграфе для удобства).

Но если нас интересует информация только о конкретном процессе и мы знаем его имя — мы можем использовать команду grep, чтобы получить только нужные строки:

1alice@ubuntu:~$ ps aux | grep "sshd"
2root         973  0.0  0.3  12020  7936 ?        Ss   Oct11   0:22 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
3root       89128  0.0  0.5  14844 10112 ?        Ss   07:42   0:00 sshd: alice [priv]
4alice      89212  0.0  0.3  15004  6808 ?        S    07:42   0:00 sshd: alice@pts/1
5root       91388  0.0  0.5  14844 10112 ?        Ss   09:59   0:00 sshd: alice [priv]
6alice      91472  0.0  0.3  15004  6808 ?        S    09:59   0:00 sshd: alice@pts/0
7alice      91600  0.0  0.1   7076  2176 pts/0    S+   10:09   0:00 grep --color=auto sshd

Давайте познакомимся ещё с одной полезной опцией — -E, которая позволяет использовать регулярные выражения.

Например, мы можем искать совпадения сразу по нескольким словам. Скажем, по слову FAIL и по слову ERROR:

1alice@ubuntu:~$ grep -Er "(ERROR|FAIL)" app.log
2ERROR: And this is another ERROR.
3FAIL: The system has failed.

Здесь (ERROR|FAIL) — регулярное выражение. Поговорим о них подробнее.

Регулярные выражения

Регулярные выражения (или regex, от англ. regular expressions) — это способ описать шаблон текста, который вы хотите найти.

Если glob-шаблоны вроде *.txt помогают искать имена файлов, то регулярные выражения позволяют искать содержимое.
С их помощью можно искать не только «где написано error», но и, например, «все строки, где встречается слово, начинающееся с заглавной буквы», или «все даты в формате 2025-10-16».

Регулярные выражения встречаются повсюду:

  • в командах Linux — grep, sed, awk, jq;
  • текстовых редакторах — VS Code, Vim, Sublime Text;
  • языках программирования — Python, JavaScript, Go, Rust и других.

Проще говоря, регулярные выражения — это универсальный язык поиска по тексту.
Например, можно задать правило для поиска «всех строк, где в начале написано def» — и тем самым вы найдёте объявления функций в Python-коде.

Пара слов о новых Linux-командах
  • sed и awk — это мощные «комбайны» для работы с текстом, которые позволяют не только искать, но и заменять текст по очень гибким шаблонам.
  • jq — позволяет работать с JSON-данными. JSON — это очень популярный формат структурированных данных, похожий на словари или объекты в языках программирования. JSON легко узнать по фигурным скобкам {} и кавычкам вокруг ключей и значений.
1{
2  "user": "alice",
3  "age": 28,
4  "active": true
5}

Примеры

Что ищем

Шаблон

Что найдёт

Любое слово, начинающееся с def

^def

Строки, где def в начале (например, объявления функций)

Любое число

[0-9]+

42, 1337, 2025

Адрес электронной почты

[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}

alice@example.com

Дату формата YYYY-MM-DD

[0-9]{4}-[0-9]{2}-[0-9]{2}

2025-10-16

Давайте рассмотрим элементы, которые используются в шаблоне:

  • ^ — указывает, что мы ищем совпадение в начале строки;
  • $ — указывает, что мы ищем совпадение в конце строки;
  • [] — задаёт диапазон символов, которые должны совпасть;
  • {n} — задаёт количество символов, которые должны совпасть; количество символов можно указывать в виде диапазона, например:
    • {2,} — два или более символа,
    • {,3} — три или менее символа;
  • + — одно или более вхождений;
  • \. — точка как символ, обратный слешу \, используется здесь для экранирования.
Пара слов о cимволах

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

В Linux \ называется escape-символом — он «снимает» специальное значение со следующего за ним символа, заставляя воспринимать его буквально. Этот процесс называется экранированием.

Эти элементы можно комбинировать, создавая достаточно сложные шаблоны, например:
^[A-Z].*[.!?]$ — найдёт строки, начинающиеся с заглавной буквы и заканчивающиеся точкой, восклицательным или вопросительным знаком.

Как построить регулярное выражение

Начнём с самого простого: найдём строки, где встречается слово error (в любом регистре):

1alice@ubuntu:~$ grep -Ei "error" app.log

А теперь — все строки, в начале которых есть слово Error (с заглавной буквы):

1alice@ubuntu:~$ grep -E "^Error" app.log

^ — означает «начало строки». То есть шаблон ^Error совпадёт только с теми строками, где Error идёт в самом начале.

Символы, из которых строятся выражения

Каждый символ в регулярном выражении — это правило. Можно комбинировать их, чтобы построить нужный шаблон.

Символ

Что значит

Пример

Что найдёт

.

Любой символ

gr.p

grep, grap, gr-p

*

0 или больше повторений

ab*c

ac, abc, abbc, abbbc

+

1 или больше повторений

ab+c

abc, abbc, но не ac

?

0 или 1 повторение

colou?r

color, colour

[ ]

Набор символов

[ab]

a или b

{n}

Количество повторений

[0-9]{4}

Четыре цифры подряд (1234, 9876)

{n,m}

Количество повторений в диапазоне

[0-9]{1,3}

От одной до трёх цифр подряд (1, 321)

^

Начало строки

^ERROR

Строки, начинающиеся с ERROR

$

Конец строки

end$

Строки, заканчивающиеся на end

\

Экранирование символа, следующего за слешем

\+[A-Za-z]{2}

Строки, содержащие плюс и две буквы подряд (+ab, +OK)

\b

Границы слова

\berror\b

Слово error, но не errors или supererrors

Поиск года в тексте

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

Для начала подумаем, какое правило стоит сформулировать:

  • Мы можем начать с простого 2025. Это тоже шаблон, но под него подойдёт только один конкретный год.
  • Мы можем использовать . в виде 20.., тогда к этому шаблону подойдут годы вроде 2024 или 2000, но также в него войдут и буквы: 20aA, что нам не очень подходит.
  • Аналогичным образом откажемся от символов * и +: они тоже включают буквы, плюс мы не уложимся в длину из четырёх цифр, характерных для года.
  • Самый близкий по смыслу шаблон здесь — это []. В нём мы можем описать, что именно мы хотим получить. Например, шаблон [0-9] включает любую цифру, но не буквы — то, что нам нужно!

То есть наш шаблон для поиска года, начинающегося с 20, будет выглядеть так:

120[0-9][0-9]

Очень неплохо! Но мы можем упростить его, указав количество повторений через {}. Мы знаем, что нам нужны ровно две цифры, подходящие под этот шаблон и расположенные одна за другой, поэтому мы можем использовать {2}:

120[0-9]{2}

Если мы хотим найти строки, которые будут начинаться с года или содержать год в конце, мы можем добавить символы ^ и $:

1^20[0-9]{2}
2# 2025 — отличный год для изучения Linux
120[0-9]{2}$
2# Отличный год для изучения Linux, 2025

Если мы хотим, чтобы строка начиналась с 2025 и заканчивалась на 2025 (то есть содержала только 2025), мы можем использовать оба символа:

1^20[0-9]{2}$
2# 2025

Мы ищем 2025 в любом месте строки, поэтому не будем использовать ^ и $. Давайте посмотрим, как наше выражение выглядит в итоге в команде grep:

1alice@ubuntu:~$ grep -E "20[0-9]{2}" report.txt
2Report for 2023
3Data updated on 2021-09-15
4Here is the data: 12013 lines of code in the lab

Идеально! Мы научились искать годы в тексте!
Но обратите внимание: под наш шаблон также попало число 12013.

Ничего страшного. Мы можем использовать границы слова \b, чтобы наш год всегда был целым словом, а не частью длинного числа

Наша итоговая команда с выражением будет выглядеть так:

1alice@ubuntu:~$ grep -E "\b20[0-9]{2}\b" report.txt
2Report for 2023
3Data updated on 2021-09-15

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

Регулярные выражения или glob-шаблоны

Многие путают регулярные выражения и glob-шаблоны. Они действительно похожи на вид, но работают на разных уровнях системы.

Glob используется оболочкой Bash для подстановки имён файлов — до запуска команды.

Например:

1ls *.txt

Bash сам подставит все подходящие файлы (notes.txt, todo.txt) и только потом вызовет ls.

Регулярные выражения обрабатываются самой программой — уже после её запуска.

1grep -E ".*\.txt" filelist.txt

Здесь grep сам ищет строки, которые содержат .txt.

Давайте проверим, как вы разобрались с поиском текста в файлах:

Чтобы добавить в заметки выделенный текст, нажмите Ctrl + E
Проверь себяСоедините порядковый номер со словом

Подсчёт строк и слов: команда wc

Мы уже умеем искать файлы (find) и находить нужные строки (grep), но иногда надо просто посчитать: сколько строк в файле, сколько слов или сколько байтов занимает текст.
Для этого в Linux есть команда wc — сокращение от word count (подсчёт слов).

1alice@ubuntu:~$ wc file.txt
2     42    250   1932 file.txt

Вывод для файла file.txt состоит из трёх чисел:

Число

Что означает

42

Количество строк

250

Количество слов

1932

Количество байтов (символов)

Команда полезна, когда нужно быстро оценить объём текста или количество записей, например строк в логе или строк данных в таблице CSV.

Если нужно вывести только одно из значений, можно добавить опции:

Опция

Что считает

Пример

Результат

-l

Строки

wc -l file.txt

42

-w

Слова

wc -w file.txt

250

-c

Байты (символы)

wc -c file.txt

1932

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

1alice@ubuntu:~$ grep "ERROR" app.log | wc -l
215

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

1alice@ubuntu:~$ find . -name "*.py" | wc -l
227

Так мы получаем число файлов с расширением .py в текущем каталоге и во всех подкаталогах.

Аналогичным образом мы можем комбинировать команду wc с cat и less.
Например, мы можем посмотреть, сколько строк в системном файле /etc/passwd (фактически сколько пользователей есть в системе):

1alice@ubuntu:~$ cat /etc/passwd | wc -l

А если указать в аргументах сразу несколько файлов, wc покажет статистику по каждому из них, а также суммарное значение:

1alice@ubuntu:~$ wc report1.txt report2.txt
2  10   56  423 report1.txt
3  14   98  703 report2.txt
4  24  154 1126 total

wc — одна из тех утилит, которые раскрываются именно в сочетании с другими командами.

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

Сравнение при помощи diff

Иногда нужно понять, чем именно отличаются два файла, например две версии одного документа, два скрипта, две конфигурации.
Для этого в Linux есть команда diff (англ. difference). Она построчно сравнивает два файла и показывает только различия.

Давайте сравним два файла — file1.txt и file2.txt:

1alice@ubuntu:~$ diff file1.txt file2.txt
21c1
3< Hello world
4---
5> Hello Linux

Что мы видим:

Символ

Значение

<

Строка из первого файла (file1.txt)

>

Строка из второго файла (file2.txt)

Строка 1c1 означает, что в первой строке первого файла (1) нужно внести изменение (c — англ. change), чтобы она стала такой же, как первая строка второго файла (1).

Формат, в котором мы получили разницу между файлами, может показаться не очень дружелюбным, но мы можем сделать его более читаемым, добавив опцию -u — для вывода в унифицированном (англ. unified) формате.

Он показывает изменения в контексте — с несколькими строками до и после отличий, как это делают системы контроля версий вроде Git.

1alice@ubuntu:~$ diff -u file1.txt file2.txt
2--- file1.txt
3+++ file2.txt
4@@ -1 +1 @@
5-Hello world
6+Hello Linux

Теперь видно сразу, что именно изменилось: строка с - была удалена, строка с + добавлена.
Такой формат используется в патчах (.patch-файлах) — текстовых файлах с разницей между версиями, которые используются в системах контроля версий кода Git, — поэтому стоит привыкнуть к нему.

Обозначения в diff -u

Символ

Что значит

---

Имя и версия первого файла

+++

Имя и версия второго файла

@@

Диапазон строк, где найдено отличие

-

Строка, присутствующая только в первом файле

+

Строка, присутствующая только во втором файле

Давайте сравним другие файлы, с большим количеством строк. У нас есть два файла — old.txt и new.txt:

1alice@ubuntu:~$ cat old.txt
2Apple
3Banana
4Cherry
5Date: 2024
6
7alice@ubuntu:~$ cat new.txt
8Apricot
9Banana
10Cherry
11Date: 2025

Сравним их при помощи команды diff с опцией -u для большей наглядности:

1alice@ubuntu:~$ diff -u old.txt new.txt
2--- old.txt
3+++ new.txt
4@@ -1,4 +1,4 @@
5-Apple
6+Apricot
7 Banana
8 Cherry
9-Date: 2024
10+Date: 2025

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

Сравнение каталогов

diff умеет сравнивать не только файлы, но и целые каталоги:

1alice@ubuntu:~$ diff -r dir1/ dir2/

Опция -r (англ. recursive) заставляет diff проходить все подкаталоги и файлы внутри них.
В результате вы увидите, какие файлы совпадают, какие отличаются, а какие есть только в одном из каталогов.

1Only in dir1/: config.old
2Only in dir2/: new.txt
3Files dir1/settings.ini and dir2/settings.ini differ

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

diff — это инструмент для анализа изменений.

Он не вносит изменения сам, а показывает, где и что отличается.
Благодаря ему вы можете точно увидеть, какие строки добавлены, удалены или изменены, будь то текстовый документ, код или конфигурация.
А ещё при помощи diff можно быстро сравнивать каталоги.

Давайте проверим, как вы разобрались с подсчётом и сравнением файлов:

Чтобы добавить в заметки выделенный текст, нажмите Ctrl + E
Проверь себяСоедините порядковый номер со словом

Что дальше

Теперь, когда вы уверенно умеете искать файлы (find), строки в тексте (grep) и быстро получать подсчёты (wc), а ещё сравнивать изменения с помощью diff, самое время разобраться, куда попадает вывод команд и как им управлять. Это фундамент для автоматизации и «склейки» инструментов в конвейеры с использованием пайпов (|).

В следующем параграфе вы узнаете:

  • Что такое стандартные потоки stdin, stdout и stderr.
  • Как перенаправлять вывод команды в файл (>) и дозаписывать в конец (>>).
  • Как подавать ввод команде из файла (<).
  • Как разделять основной вывод и ошибки (2>) и писать их в разные файлы.
  • Как объединять перенаправления (например, ./script.sh > out.log 2> err.log).
  • Что такое код возврата команды и как его проверить ($?).
  • Как интерпретировать коды возврата (0 — успех, разные типы ошибок).

После этого вы сможете:

  • Собирать «журналы» работы своих команд и скриптов, разносить ошибки по отдельным файлам.
  • Строить надёжные проверки в сценариях: «если команда завершилась успешно — делай A, иначе — B».

А пока закрепите материал на практике:

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

Ключевые выводы по параграфу

  • find позволяет искать файлы и каталоги по множеству критериев: по имени (-name), типу (-type), дате изменения (-mtime), размеру (-size) и другим.
  • find может не только находить, но и выполнять действия над результатами — удалять (-delete), запускать внешние команды (-exec), в том числе с подтверждением (-ok).
  • Формы -exec {} \; и -exec {} + различаются: первая запускает команду для каждого файла, вторая — сразу для группы найденных путей и работает быстрее.
  • grep ищет строки по шаблону внутри файлов — чувствителен к регистру, но опция -i его игнорирует.
  • grep удобно использовать с пайпами (|) для фильтрации вывода других команд.
  • Регулярные выражения (regex) — это язык описания шаблонов текста; они позволяют находить сложные совпадения и применяются во множестве утилит.
  • Glob-шаблоны (*.txt, ?, []) подставляются оболочкой до выполнения команды, а регулярные выражения обрабатываются программой во время выполнения.
  • wc подсчитывает строки (-l), слова (-w) и символы (-c) — как в файлах, так и в потоках вывода других команд.
  • diff построчно сравнивает файлы и каталоги, показывает различия и умеет выводить их в формате патчей (-u), как в системах контроля версий, например Git.

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



Чтобы добавить в заметки выделенный текст, нажмите Ctrl + E
Предыдущий параграф3.6. Просмотр содержимого и архивы
Следующий параграф3.8. Потоки ввода-вывода и коды возврата