Zapobiegaj automatycznym EOF do nazwanego potoku i wysyłaj EOF, kiedy chcę

12

Mam program, który kończy działanie automatycznie po odczytaniu EOF w danym strumieniu (w następującym przypadku stdin).
Teraz chcę utworzyć skrypt powłoki, który tworzy nazwaną potok i podłącza do niego standardowe wejście programu. Następnie skrypt zapisuje do potoku kilka razy przy użyciu echoi cat(oraz innych narzędzi, które automatycznie generują EOF po wyjściu). Problem, z którym się zmagam, polega na tym, że gdy pierwszy raz się echozakończy, wysyła EOF do potoku i powoduje, że program się kończy. Jeśli użyję czegoś takiego, tail -fnie mogę wysłać EOF, gdy zamierzam wyjść z programu. Poszukuję zrównoważonego rozwiązania, ale bezskutecznie.
Znalazłem już zarówno sposoby zapobiegania EOF, jak i ręczne wysyłanie EOF, ale nie mogę ich połączyć. Czy jest jakaś wskazówka?

#!/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
źródło

Odpowiedzi:

13

Jak wskazali inni, czytnik potoku otrzymuje EOF, gdy nie ma już pisarzy. Tak więc rozwiązaniem jest upewnienie się, że zawsze jest jeden pisarz trzymający go otwartym. Ten pisarz nie musi niczego wysyłać, po prostu trzymaj to otwarte.

Ponieważ używasz skryptu powłoki, najprostszym rozwiązaniem jest powiedzenie powłoce, aby otworzyła potok do pisania. A potem zamknij go, gdy skończysz.

#!/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

Zauważ, że jeśli pominiesz ostatni wiersz, deskryptor pliku 3 zostanie automatycznie zamknięty (a zatem czytnik otrzyma EOF) po zakończeniu skryptu. Oprócz wygody zapewnia to również pewne bezpieczeństwo, jeśli skrypt zostanie w jakiś sposób wcześniej zakończony.

Patrick
źródło
2
to exec 3>Ppowoduje zawieszenie się, dlaczego?
Wang
@Wang Nie powinno. Jeśli tak, to prawdopodobnie nie robisz tego samego co kod POC w pytaniu. Jedyny powód, dla którego mogę pomyśleć, że to zablokuje to, jeśli zamiast tego robisz coś takiego exec 2>P, a masz włączony tryb śledzenia ( set -x), w którym bash będzie zapisywał na potoku, ale nie ma czytnika, więc blokuje czekanie na coś do przeczytania.
Patrick
1
@Wang @Patrick Rzeczywiście exec 3>Pwisi na mojej maszynie. Wynika to z faktu, że nie ma procesu odczytu z P. Tak więc rozwiązaniem było zamiana linii exec 3>Pi program < P &(dodanie znaku handlowego i tak, aby program działał w tle).
macieksk
2

Potok otrzymuje EOF, gdy ostatni pisarz odejdzie. Aby tego uniknąć, upewnij się, że zawsze istnieje program piszący (proces, w którym potok jest otwarty do pisania, ale tak naprawdę nic nie pisze). Aby wysłać EOF, spraw, aby pisarz rezerwowy odszedł.

mkfifo P
while sleep 1; do :; done >P &
P_writer_pid=$!
send_eof_to_P () {
  kill $P_writer_pid
}
Gilles „SO- przestań być zły”
źródło
0

Nie ma sposobu, aby program rozróżniał EOF, co oznacza „nadszedł czas, aby wyjść” od EOF, który oznacza, że ​​„program piszący jest gotowy, ale może być więcej danych od kogoś innego”.

Jeśli masz możliwość modyfikowania zachowania programu, wykonaj odczyt w nieskończonej pętli (jedna iteracja trwa do EOF) i wyślij mu określony ciąg poleceń, który oznacza „czas wyjść”. Wysłanie tego ciągu byłoby zadaniem send_eofpolecenia w twoim pytaniu.

Inna opcja:

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

lub

{ echo some stuff; cat more_stuff.txt; } >P
Kusalananda
źródło