Почему зависает терминал при запуске редактора через >(cmd) в Bash

Представьте: вы хотите быстро отредактировать файл и сразу передать его содержимое другой команде. В голову приходит хитрость с подстановкой процесса через >(cat), которая отлично работала с echo. Но стоит попробовать это с nano или vim – терминал замирает, курсор мигает, а вы остаётесь в недоумении. Что пошло не так? Давайте разберёмся шаг за шагом.

Как работают анонимные каналы в Bash

Конструкция >(команда) создаёт анонимный канал (pipe), который связывает вывод одной программы с входом другой. Например, в команде:

echo 42 > >(cat)

…Bash сначала создаёт канал, запускает cat с подключённым к нему входом, а затем перенаправляет вывод echo в этот канал. Всё работает, потому что:

  1. echo записывает данные и сразу завершается
  2. Канал закрывается, и cat получает сигнал EOF (конец файла)

Но с редакторами всё иначе. Когда вы пишете:

EDITOR=nano
$EDITOR >(cat)

…происходит следующее:

  • Создаётся канал, но он остаётся открытым – редактор начинает работать с ним как с файлом
  • Nano пытается прочитать текущее содержимое «файла» (канала), но там ещё ничего нет
  • Канал не закрыт – редактор ждёт данных бесконечно (это как стоять у пустого почтового ящика и ждать письмо, которое никогда не придёт)

Кстати, в Linux /dev/fd/## – это не прямая ссылка на дескриптор, а новый «вход» в тот же канал. Редактор открывает его заново, создавая петлю зависимости.

Почему named pipes и временные файлы – спасение

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

1. Именованные каналы (mkfifo)

mkfifo mypipe     # создаём именованный канал
cat mypipe &      # запускаем читателя в фоне
nano mypipe       # редактор будет писать в канал

Но тут есть нюанс:

  • Nano откроет канал для записи, но не закроет его автоматически
  • После сохранения файла нужно вручную послать EOF (Ctrl+D) или завершить процесс
  • Не самый удобный вариант для повседневного использования

2. Временные файлы через mktemp (рекомендуется)

TMPFILE=$(mktemp)  # создаём уникальный временный файл
nano "$TMPFILE"    # редактируем как обычно
cat "$TMPFILE"     # используем результат
rm "$TMPFILE"      # не забываем очистить

Преимущества:

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

Если очень хочется использовать каналы, можно схитрить:
(sleep 1; cat) < >(nano)
Но это рискованно – тайминги могут сбиться, а данные потеряться.

Глубже в проблему: как каналы влияют на дескрипторы

Когда вы используете >(cat), Bash:

  1. Создаёт pipe с двумя концами: запись (fd 63) и чтение (у cat)
  2. Подставляет путь вида /dev/fd/63 как аргумент для редактора
  3. Редактор открывает этот путь заново, получая новый дескриптор (fd 3)

Теперь:

  • Редактор держит оба конца канала (исходный fd 63 для записи и новый fd 3 для чтения)
  • При попытке чтения из fd 3 он ждёт данных от… самого себя через fd 63
  • Получается логическая петля – как микрофон, направленный на колонку
# Примерная схема файловых дескриптеров:
nano:
  fd 0 → tty (ввод с клавиатуры)
  fd 1 → tty (вывод в терминал)
  fd 63 → pipe (запись)
  fd 3 → pipe (чтение)

Чтобы избежать таких ситуаций, помните:

  • Анонимные каналы подходят для однонаправленных операций с быстрыми командами
  • Редакторы и интерактивные программы требуют «стабильных» файловых объектов
  • Даже если обойти блокировку (например, через dd), данные могут теряться при неаккуратном использовании

Совет из практики: если нужно быстро отредактировать вывод команды, используйте vim <(curl example.com) – здесь работает подстановка через <(cmd), которая создаёт временный файл с содержимым вывода. Но для сохранения изменений всё равно потребуется явно указать куда писать результат.

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

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

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