Порядок вывода с заменой процесса

11

Это то , что я обычно делаю , чтобы работать grepи wcна файл , без необходимости сканирования дважды

<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null

Тем не менее, это дает

EXEC LITERAL
32

иногда и

32
EXEC LITERAL

в другие времена. (Выходные данные grepпредшествуют выходным wcданным в первом случае и наоборот во втором.)

С другой стороны, с перенаправлениями и файловыми дескрипторами

{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1 

Кажется, я всегда

EXEC LITERAL
32

Я предпочитаю, чтобы порядок вывода был предсказуемым, но гарантирован ли он при втором подходе?

Iruvar
источник

Ответы:

4

В обоих

<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null

И:

{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1

Все tee, grepи wcзапускаются одновременно. Что имеет значение тогда, это то, что происходит в конце.

wcбудет печатать результат только тогда, когда он видит конец файла на своем стандартном вводе. В первом случае это когда teeвыход, потому что затем teeзакроет его fdна другом конце канала, из которого wcвыполняется чтение (запускается путем подстановки процесса). Нет никакой гарантии, что grepк этому времени будут прочитаны все входные данные, не говоря уже о записанных выходных данных (учитывая, что каналы могут содержать довольно большой объем данных и, wcвероятно, будут быстрее, чем grep)

Во втором случае wcувидит конец файла, когда все записывающие в канал, из которого он читает, закрыли свой конец канала. В этом случае, хотя, есть несколько авторов. tee(через свой открытый fd /dev/fd/3и через свой fd 3) и grepкоторый также имеет свои fd3 открытые для канала wc(хотя он не использует его, не говоря уже о записи в него). Внутренний {, скорее всего, вызовет дополнительный подоболочечный процесс, который также будет иметь fd3 открытых и будет ждать и того, teeи другого grep.

Это означает, что он wcбудет писать свой номер строки только после grepвыхода.

Если бы вы написали это правильно, то есть закрыв fds, который не нужно открывать:

{ { <file.txt tee /dev/fd/3 4>&- | 
   grep LITERAL >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1

Тогда заказ не будет гарантирован в оболочках, которые оптимизируют процесс подоболочек. Тем не менее, только оболочка , что я знаю , что делает это , ksh93но ksh93использует сокет пар для труб, поэтому /dev/fd/3не будет работать там на Linux , по крайней мере.

Чтобы увидеть, какие процессы запущены, вы можете заменить grepна ps:

$ { { <file.txt tee /dev/fd/3 4>&- | ps -H >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1
  PID TTY          TIME CMD
 8727 pts/5    00:00:00 bash
 8815 pts/5    00:00:00   bash
 8817 pts/5    00:00:00     tee
 8818 pts/5    00:00:00     ps
 8816 pts/5    00:00:00   wc

С помощью bashвы можете увидеть этот дополнительный процесс оболочки, и вы также можете увидеть, что канал открыт на fd 3 с помощью:

$ (p=$BASHPID; { { <file.txt tee /dev/fd/3 4>&- | lsof -ag "$p" -d3 >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1)
COMMAND  PID PGID     USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
bash    9843 9842 chazelas    3w  FIFO    0,8      0t0 153304 pipe
tee     9845 9842 chazelas    3w  FIFO    0,8      0t0 153304 pipe
lsof    9846 9842 chazelas    3r   DIR    0,3        0      1 /proc
Стефан Шазелас
источник
Благодарю. В вашем «правильном примере», что grep LITERAL >&4 3>&- 4>&-означает, что fd 4, кажется, используется и закрыт?
iruvar
@ 1_CR, после >&4, сокращенно 1>&4, grepfd 1 и 4 указывают на один и тот же ресурс (начальный стандартный вывод оболочки). grepне нужно, чтобы его fd 4 был открыт для чего-либо. Он ничего с этим не делает, поэтому мы закрываем его4>&-
Стефан Шазелас
Последняя командная строка - загадочная магия.
-1

Для получения предсказуемого заказа используйте

(<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null)|sort
Торстен Стэрк
источник
Возможно, я не был достаточно ясен. Я имел в виду предсказуемый порядок с точки зрения порядка вывода команды (т.е. вывод из grep до вывода из wc). Мне не нужно сортировать объединенный вывод
iruvar
только что обнаружил gnu.org/software/bash/manual/bashref.html#Command-Grouping , он говорит мне, что с помощью операторов {} вы гарантируете (в этом случае), что вы сначала делаете <file.txt tee / dev / fd / 3 | grep LITERAL> & 4; и когда это будет сделано, вы позвоните в wc, так что, чтобы ответить на ваш первоначальный вопрос, да, насколько я понимаю, это гарантировано
Торстен Стерк
1
@ThorstenStaerk Не могли бы вы добавить дополнительную информацию, которую вы нашли в своем ответе?
Terdon