Вы когда-нибудь задумывались, как команды вроде diff
и patch
«договариваются» между собой? Допустим, у вас есть два файла с фруктами – FileA и FileB. Вы сравниваете их через diff
, сохраняете различия, а потом «накатываете» изменения на исходник. Но когда дело доходит до перенаправления ввода (этот странный символ 0), возникает путаница. Давайте разберёмся, как это работает на самом деле, и почему терминал ведёт себя не так, как кажется.
От теории к практике: что происходит под капотом
Представим, что FileA содержит список:
- Яблоко
- Груша
- Банан
А FileB выглядит так:
- Яблоко
- Апельсин
- Банан
Команда diff fileA fileB > file.diff
создаст файл с инструкциями для patch
. Если открыть file.diff, увидите что-то вроде:
2c2
Апельсин
Теперь главный вопрос: почему patch fileA (или с 0) применяет изменения, хотя ввод по умолчанию – клавиатура?
Секрет в том, что перенаправление временно заменяет «источник» ввода. Вместо /dev/pts/0 (ваш терминал) система использует файл. Это как подменить микрофон на запись с диктофона.
Как работают файловые дескрипторы
В Linux всё – файл, даже устройства ввода-вывода. Когда вы запускаете команду:
- Стандартный ввод (stdin) получает дескриптор 0.
- Стандартный вывод (stdout) – 1.
- Ошибки (stderr) – 2.
Оператор 0 явно указывает: «Возьми данные для дескриптора 0 (stdin) из файла». Но поскольку 0 – значение по умолчанию для ввода, можно писать просто. Обе команды идентичны:
patch fileA
Проверить, откуда идёт ввод, можно через tty
и сравнение inode. Например:
# Без перенаправления
if [[ /dev/fd/0 -ef /dev/pts/0 ]]; then echo "Ввод с терминала"; fi
# С перенаправлением
if [[ /dev/fd/0 -ef file.diff ]]; then echo "Ввод с файла"; fi
Во втором случае условие вернёт true
, потому что /dev/fd/0
теперь ссылается на file.diff.

На скриншоте видно, как /dev/fd/0 указывает на терминал (/dev/pts/0), когда перенаправления нет.
Частые ошибки и как их избежать
- Пути к файлам. Если
patch
ругается на «Can’t find file», проверьте, что запускаете команду из нужной директории. Кстати, можно указать полный путь:patch /home/user/fileA .
- Формат diff-файла. Некоторые версии
diff
создают вывод, несовместимый сpatch
. Используйте опцию-u
для унифицированного формата:diff -u fileA fileB > file.diff
. - Права доступа. Убедитесь, что у вас есть права на запись в fileA. Если нет – добавьте через
chmod +w fileA
.
Одна из неочевидных проблем: если в процессе выполнения patch
запрашивает подтверждение (например, при конфликтах), а вы перенаправили ввод, программа «зависнет». Чтобы этого избежать, используйте опцию --batch
для автоматического принятия решений:
patch --batch fileA
После перенаправления /dev/fd/0 указывает на file.diff, что видно на втором скриншоте.
Иногда полезно посмотреть, куда именно ведут симлинки. Команда ls -l /dev/fd/
покажет текущие связи. Если видите что-то вроде /dev/fd/0 -> /path/to/file.diff
, значит, перенаправление работает корректно.
Запомните: перенаправление ввода – это не магия, а замена «источника» данных. Терминал, файл, даже вывод другой программы (через пайпы) – всё это можно использовать как stdin.