LINUX.ORG.RU

Что не так с именованным пайпом?

 , ,


0

2

Тестирую простой пример:

mkfifo something123.txt

cat something123.txt | gzip -7 -c > out.gz &

echo "foo" > something123.txt
sleep 1
echo "bar" > something123.txt
sleep 2
echo "baz" > something123.txt
sleep 1
rm something123.txt

Ожидаю, что когда распакую out.gz - увижу в нём три строки foo-bar-baz. По факту терминал зависает после первой записи в пайп (foo). Если прервать операцию - в распакованном out.gz видим только запись о foo (bar нету). Такое чувство, что пайп закрылся после foo.

А если переписать вот так, то всё начинает работать, как мной ожидалось:

exec 3> >(gzip -7 -c > out.gz)

echo "foo" >&3
sleep 1
echo "bar" >&3
sleep 2
echo "baz" >&3
sleep 1

exec 3>&-

…терминал не зависает, создаётся файлик out.gz, который распаковываем и получаем в нём три строки:

gunzip < out.gz > out.txt && cat out.txt
foo
bar
baz

Я что-то неправильно понимаю про именованный пайп и в первом случае ожидаю от него чего-то ему не свойственного?

Так и распаковывай в именованный пайп и потом из него читай по другую сторону вертикальной чёрточки.

Exmor_RS ★★★
()
Ответ на: комментарий от Exmor_RS

Не понял вас. Но вот так уже работает почти хорошо (я ошибся в стрелке «>» в первом примере, вместо неё надо «&>»):

mkfifo something123.txt

cat something123.txt | gzip -7 -c > out.gz &


echo "foo" >& something123.txt
sleep 1
echo "bar" >& something123.txt
sleep 2
echo "baz" >& something123.txt
sleep 1
rm something123.txt

Так в итоговый файл пишутся все три строки. Но проблема остаётся в зависающем терминале. И «файл» пайпа не удаляется и остаётся висеть, т.е. до последней строки «rm something123.txt» скрипт по какой-то причине не доходит.

Novascriptum
() автор топика
Ответ на: комментарий от Novascriptum

Он всегда останется висеть пока не удалишь его.

Из него читать надо, чтобы он работал:

gzip -7 -c > named_fifo | cat named_fifo > my_results.txt

Exmor_RS ★★★
()
Ответ на: комментарий от Novascriptum

Нет, поторопился я с выводами. Так же зависает после foo…

Novascriptum
() автор топика
Ответ на: комментарий от Exmor_RS

У меня есть сложный скрипт с кучей параметров, который динамически формирует разные части бинарных данных. И потом эти данные надо куда-то отправить. Я хотел так:

# тут как-то инициируем named_pipe
...
# дальше в коде (это сильное упрощение) вызываем скрипт с разными параметрами, и чтобы они все писали в один и тот же поток
launsh1.sh --parameter1 --parameter2 ...--parameterN &> named_pipe
launch2.sh --parameter1 --parameter2 ...--parameterN &> named_pipe
launch3.sh --parameter1 --parameter2 ...--parameterN &> named_pipe

# в итоге получаем объединённые данные

Варианты объединения результата типа:

{ launch1.sh; launch2.sh; launch3.sh; } | ...

…мне не очень подходят из-за того, что эти запуски происходят в цикле и со сложным конструированием этих самых параметров. Хотел максимально упростить.

Меня в целом-то устраивает тот вариант с 3-им файловым дескриптором в самом первом сообщении, что я привёл. Просто непонятно, почему именованный пайп блокируется. Хотелось разобраться.

Novascriptum
() автор топика
Последнее исправление: Novascriptum (всего исправлений: 3)

Всё просто.

Вы запускаете в фоне процесс, который производит чтение из FIFO:

cat something123.txt | gzip -7 -c > out.gz &

Поскольку читать пока нечего, операция чтения блокируется.

Далее вы выполняете следующую команду:

echo "foo" > something123.txt

При её выполнении происходит следующее:

  • оболочка открывает файл something123.txt на запись
  • оболочка устанавливает дескриптор этого файла в качестве стандартного вывода
  • как только команда завершается, файл something123.txt закрывается
  • поскольку все пишущие в FIFO дескрипторы закрыты, читающий из него процесс cat получает EOF и завершается

Далее вы пытаетесь записать новую строку в FIFO, однако он уже не открыт на чтение, поэтому вызов записи блокируется.

Rootlexx ★★★★★
()

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

Rootlexx ★★★★★
()
Последнее исправление: Rootlexx (всего исправлений: 1)

Подумай логически, как gzip поймёт, что поток закончился, а не будет вечно ждать следующие байты после baz. Нет, не по удалению файла. На Windows ты бы не смог удалить открытый файл, на Linux ты можешь удалить файл, но его фактическое удаление с диска произойдёт только после его закрытия программой. То есть другая программа не сможет, конечно, открыть пайп, но те кто уже открыл от удаления ничего не почувствуют как не почувствовали бы и при удалении обычного файла.

Конец данных в пайпе определяется по закрытию его дескриптора другой стороной. Что из себя представляет echo с перенаправлением в файл? Ничто иное как последовательность open-write-close. Так что после первого echo gzip получит признак конца файла и тоже завершится, сохранив результат. Следующий echo столкнётся с проблемой, что отсутствует принимающая сторона и будет, соответственно, ждать, пока кто-нибудь откроет пайп на чтение. А этого никто не делает.

KivApple ★★★★★
()
Ответ на: комментарий от Novascriptum

Попробуй дополнительной эхой делать перед удалением фифо-файла:
echo > something123.txt

Сам по себе echo close не делает видимо (встроенная команда оболочки всё таки).

Exmor_RS ★★★
()
Последнее исправление: Exmor_RS (всего исправлений: 1)
Ответ на: комментарий от Rootlexx

Тоже хотел это написать. В этом плане fifo работают очень необычно, в сравнении со всеми остальными файловыми дескрипторами.

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

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

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 3)

Use tail -f, Luke

-cat

+cat &
+PID=$!
+tail -f --pid=$PID ...
...
+kill $PID

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 2)

А если переписать вот так, то всё начинает работать, как мной ожидалось:

Там у тебя субшелл.
А до этого cat завершается после первого echo.

crutch_master ★★★★★
()
Ответ на: комментарий от firkax

А ты запускал пример?

Я вот как у автора сделал, изначальный cat отправленный в фон завершился только после отправки «пустой» эхи, и его архив нормально собрался.

Exmor_RS ★★★
()
Ответ на: комментарий от Exmor_RS

У автора cat завершается после первого же (не пустого) echo. А он спрашивал как сделать чтобы не завершался. От добавления команд в конец очевидно ситуация в начале измениться не может.

firkax ★★★★★
()
Ответ на: комментарий от Exmor_RS

Нет, завершался. «Подвисал» это вообще чушь, он либо ждёт данных, либо читает данные, либо завершается. Прочти ещё раз всё что тут писали.

firkax ★★★★★
()

cat при чтении не с терминала, а из файла/пайпа буфферизует не построчно, а по блочно, потому пока весь блок не прочитается запись второй строки невозможна. Так что формируйте либо большими блоками данные, либо объединяйте в одну запись, которая начнется после окончания записи:

mkfifo something123.txt

cat something123.txt | gzip -7 -c > out.gz &

{
echo "foo"
sleep 1
echo "bar"
sleep 2
echo "baz";
sleep 1
} > something123.txt
rm something123.txt

vodz ★★★★★
()
Ответ на: комментарий от firkax

Да похоже таки закрывается.
Слип тоже влияет.
Вообще похоже cat тут не самое оптимальное решение.

Exmor_RS ★★★
()
Ответ на: комментарий от crutch_master

Ну тогда так:

mkfifo something123.txt

cat something123.txt | gzip -7 -c > out.gz &

exec > something123.txt

echo "foo"
sleep 1
echo "bar"
sleep 2
echo "baz"
sleep 1

rm something123.txt

vodz ★★★★★
()
Ответ на: комментарий от firkax

Только автор не хочет stdout перенаправлять

Хочет. Судя по всему второй пример он откуда-то спер и не понимает там ничего.

rm можно сразу делать

При однократном открытии — да, но принято не тереть то что пока работает, это служит дополнительным индикатором, что пока идёт работа.

vodz ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.