Запретите автоматические EOF в именованный канал и отправьте EOF, когда я этого захочу

12

У меня есть программа, которая автоматически выходит при чтении EOF в данном потоке (в следующем случае, stdin).
Теперь я хочу создать сценарий оболочки, который создает именованный канал и подключает к нему stdin программы. Затем скрипт записывает в канал несколько раз, используя echoи cat(и другие инструменты, которые автоматически генерируют EOF при выходе). Проблема, с которой я сталкиваюсь, заключается в том, что, когда первое echoсделано, оно отправляет EOF в канал и завершает работу программы. Если я использую что-то вроде tail -fэтого, я не могу отправить EOF, когда я собираюсь выйти из программы. Я исследую сбалансированное решение, но безрезультатно.
Я уже нашел, как предотвратить EOF и как вручную отправить EOF, но я не могу объединить их. Есть ли подсказка?

#!/bin/sh
mkfifo P
program < P & : # Run in background
# < P tail -n +1 -f | program
echo some stuff > P # Prevent EOF?
cat more_stuff.txt > P # Prevent EOF?
send_eof > P # How can I do this?
# fg
iBug
источник

Ответы:

13

Как указали другие, читатель канала получает EOF, как только не осталось авторов. Таким образом, решение состоит в том, чтобы убедиться, что всегда один писатель держит его открытым. Этому писателю не нужно ничего отправлять, просто держите его открытым.

Поскольку вы используете сценарий оболочки, самое простое решение - сказать оболочке открыть канал для записи. А затем закройте его, когда закончите.

#!/bin/sh
mkfifo P
exec 3>P # open file descriptor 3 writing to the pipe
program < P
# < P tail -n +1 -f | program
echo some stuff > P
cat more_stuff.txt > P
exec 3>&- # close file descriptor 3

Обратите внимание, что если вы пропустите последнюю строку, дескриптор файла 3 будет автоматически закрыт (и, таким образом, читатель получит EOF), когда скрипт завершится. Помимо удобства, это также обеспечивает некоторую безопасность, если сценарий должен был каким-либо образом завершиться рано.

Патрик
источник
2
это exec 3>Pпричина зависания в bash, почему?
Ван
@ Ван Это не должно быть. Если это так, то вы, вероятно, не делаете то же самое, что и код POC в вопросе. Единственная причина, по которой я могу думать, что это заблокирует, - это если вы вместо этого делаете что-то подобное exec 2>P, и у вас включен режим трассировки ( set -x), в котором bash собирается записывать в канал, но читателя нет, поэтому он блокирует ожидание что-то почитать.
Патрик
1
@ Wang @Patrick exec 3>PНа моей машине тоже висит bash. Это потому, что нет процесса чтения из P. Таким образом, решением было поменять строки exec 3>Pи program < P &(добавив амперсанд так, чтобы программа работала в фоновом режиме).
Macieksk
2

Труба получает EOF, когда последний писатель уходит. Чтобы избежать этого, убедитесь, что всегда есть писатель (процесс, у которого канал открыт для записи, но на самом деле ничего не пишет). Чтобы отправить EOF, заставьте этого резервного автора уйти.

mkfifo P
while sleep 1; do :; done >P &
P_writer_pid=$!
send_eof_to_P () {
  kill $P_writer_pid
}
Жиль "ТАК - перестань быть злым"
источник
0

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

Если у вас есть возможность изменить поведение вашей программы, выполните чтение в бесконечном цикле (одна итерация длится до EOF) и отправьте ей специальную командную строку, которая означает «время для выхода». Отправка этой строки будет задачей send_eofкоманды в вашем вопросе.

Другой вариант:

( echo some stuff; cat more_stuff.txt ) >P

или

{ echo some stuff; cat more_stuff.txt; } >P
Кусалананда
источник