Чтение и сравнение файлов: cat, less, tail, diff и cmp
Автор: Казачкин Даниил Михайлович · Обновлено
Просмотр файла, наблюдение за его ростом и сравнение двух версий — разные операции. Cat удобно передаёт всё содержимое, less позволяет изучать большой текст, tail следит за…
Просмотр файла, наблюдение за его ростом и сравнение двух версий — разные операции. Cat удобно передаёт всё содержимое, less позволяет изучать большой текст, tail следит за концом, diff объясняет изменение строк, cmp сравнивает байты. Выбор инструмента влияет на читаемость и на то, что именно будет проверено.
Небольшой воспроизводимый файл
reading_lab=$(mktemp -d)
printf '%s\n' alpha beta gamma >"$reading_lab/a.txt"
printf '%s\n' alpha BETA gamma >"$reading_lab/b.txt"
cat "$reading_lab/a.txt"
head -n 2 "$reading_lab/a.txt"
tail -n 1 "$reading_lab/a.txt"
wc -l -c "$reading_lab/a.txt"Cat выводит три строки, head — alpha и beta, tail — gamma. wc -l считает символы перевода строки: здесь их три; wc -c получает 17 байт. Если последняя строка не завершена переводом строки, визуальное число строк и результат wc -l могут различаться. В UTF-8 число байтов не обязано совпадать с числом символов; для символов есть wc -m с подходящей locale.
Команда cat без пути читает stdin. Если случайно запустить её в терминале, Ctrl+D на пустой строке сообщает EOF, а Ctrl+C прерывает выполнение. cat a.txt b.txt просто объединяет потоки, не вставляя заголовки или разделители. Если нужен поиск, grep 'beta' a.txt читает файл напрямую; cat a.txt | grep beta уместен для понимания конвейера, но здесь лишний процесс.
Less для большого файла
Откройте less "$reading_lab/a.txt". Клавиши g и G переводят в начало и конец, /beta ищет вперёд, ?alpha — назад, n повторяет поиск, q выходит. Стрелки и Page Up/Page Down двигают окно. less -N добавляет номера строк. Название less не задаёт лимит размера, но поиск по большому файлу всё равно требует чтения данных.
More часто ориентирован на последовательное перелистывание, а less удобнее для движения назад, поиска и просмотра изменяющегося файла. Наличие обеих программ и детали клавиш зависят от реализации. Не открывайте неизвестные бинарные данные через cat в терминал: управляющие последовательности могут испортить отображение. При сломанном режиме терминала сначала завершите программу, затем используйте штатный reset; причина может быть в данных, а не в кодировке всей системы.
Tail: следить за дескриптором или именем
tail -f app.log в GNU tail по умолчанию продолжает читать открытый файловый дескриптор. Если приложение переименует старый журнал и создаст новый файл под прежним именем, tail может остаться на старом объекте. tail -F app.log означает слежение по имени с повторными попытками открытия и полезен при ротации. Ни один вариант не восстанавливает записи, которые приложение не записало.
В первом терминале запустите:
tail -n 0 -F "$reading_lab/a.txt"Во втором передайте фактический путь стенда и допишите строку printf '%s\n' delta >>ПУТЬ/a.txt. Первый терминал покажет только добавленную delta. Остановите просмотр Ctrl+C. Если файл временно исчезает, -F сообщает об этом и продолжает ожидание; сообщение в stderr не означает, что follow завершился. Для systemd-службы журнал может вообще находиться в journald: тогда нужен journalctl из отдельного урока.
Diff и cmp: изменение текста и различие байтов
diff -u "$reading_lab/a.txt" "$reading_lab/b.txt"
printf 'diff status=%s\n' "$?"
cmp "$reading_lab/a.txt" "$reading_lab/b.txt"
printf 'cmp status=%s\n' "$?"На исходных файлах diff показывает замену beta на BETA и возвращает 1. Для diff статус 0 означает отсутствие различий, 1 — найденные различия, больше 1 — проблему выполнения. Такой статус 1 не означает сломанную утилиту. Cmp сообщает первое несовпадение на седьмом байте во второй строке; cmp -s оставляет только статус. Если вы уже дописали delta, будет и дополнительное различие: результат зависит от состояния fixture.
diff -r каталог1 каталог2 сравнивает деревья, но не доказывает совпадение владельцев, ACL или всех метаданных. Для проверки целостности скачанного артефакта удобен sha256sum с доверенным ожидаемым digest. Одинаковая контрольная сумма не подтверждает подлинность источника сама по себе.
Бинарные данные и here-document
printf '\x41\x00\x42\x0a' >"$reading_lab/bytes.bin"
od -An -tx1 "$reading_lab/bytes.bin"
strings -n 1 "$reading_lab/bytes.bin"
cat >"$reading_lab/note.txt" <<'NOTE'
Строка с буквальным $HOME
NOTEOd показывает 41 00 42 0a; xxd, если установлен, даёт другой удобный шестнадцатеричный вид. Strings извлекает читаемые последовательности и теряет часть структуры — его вывод не является полным содержимым файла. Here-document заканчивается строкой NOTE; кавычки у первого NOTE запрещают раскрытие $HOME. Маркер завершения должен стоять отдельно, без случайных пробелов. Подробности перенаправлений разобраны в уроке о tee.
Практика и диагностика
Сделайте копию файла, измените одну строку и получите unified diff. Затем сравните одинаковые копии и подтвердите статус 0. Создайте текст без конечного перевода строки и объясните отличие wc -l от числа видимых записей. Для бинарного fixture проверьте наличие нулевого байта через od.
Если вывод пуст, сначала проверьте stat и используемые параметры: tail -n 0 показывает только будущие данные, grep может отфильтровать всё, less может открыться в конце. Если чтение запрещено, исследуйте права на файл и родительские каталоги, а не меняйте права всего дерева. Закончив, остановите фоновые просмотрщики и удалите только "$reading_lab".