Как работает перенаправление ввода в Linux: разбор на примере diff и patch

Вы когда-нибудь задумывались, как команды вроде diff и patch «договариваются» между собой? Допустим, у вас есть два файла с фруктами – FileA и FileB. Вы сравниваете их через diff, сохраняете различия, а потом «накатываете» изменения на исходник. Но когда дело доходит до перенаправления ввода (этот странный символ 0), возникает путаница. Давайте разберёмся, как это работает на самом деле, и почему терминал ведёт себя не так, как кажется.

От теории к практике: что происходит под капотом

Представим, что FileA содержит список:

  • Яблоко
  • Груша
  • Банан

А FileB выглядит так:

  • Яблоко
  • Апельсин
  • Банан

Команда diff fileA fileB > file.diff создаст файл с инструкциями для patch. Если открыть file.diff, увидите что-то вроде:

2c2  
 Апельсин

Теперь главный вопрос: почему patch fileA (или с 0) применяет изменения, хотя ввод по умолчанию – клавиатура?

Секрет в том, что перенаправление временно заменяет «источник» ввода. Вместо /dev/pts/0 (ваш терминал) система использует файл. Это как подменить микрофон на запись с диктофона.

Как работают файловые дескрипторы

В Linux всё – файл, даже устройства ввода-вывода. Когда вы запускаете команду:

  1. Стандартный ввод (stdin) получает дескриптор 0.
  2. Стандартный вывод (stdout) – 1.
  3. Ошибки (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.

Добавить комментарий

Все поля обязательны к заполнению. Ваш адрес email не будет виден никому.

Новое
Интересное