Гарантирован ли порядок, который тройник печатает на стандартный вывод? - PullRequest
3 голосов
/ 29 июня 2010

Вы можете разделить канал, используя команду tee под linux следующим образом:

printf "line1\nline2\nline3\n" | tee >(wc -l ) | (awk '{print "this is awk: "$0}')

, что дает вывод

this is awk: line1
this is awk: line2
this is awk: line3
this is awk: 3

Мой вопрос:этот порядок печати гарантирован?Будет ли в конце всегда печататься разделенная труба tee, которая считает количество строк?Есть ли способ всегда печатать его в начале?Или порядок печати tee никогда не гарантируется?

Ответы [ 3 ]

2 голосов
/ 29 июня 2010

Это не определено tee, но, как говорит Daenyth, wc не будет завершен, пока tee не закончит передачу данных - так что обычно tee тоже будет передавать его в awk.В этом случае может быть лучше, чтобы awk сделал подсчет.

echo -ne {one,two,three,four}\\n | \
awk '{print "awk processing line " NR ": "$0} END {print "Awk saw " NR " lines"}'

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

Существуют более продвинутые методы конвейеризации, такие как bash-копроцессы (coproc) или именованные каналы (mkfifo или mknod).п).Последний получает ваши имена в файловой системе, которые могут быть переданы другим процессам, но вам придется их очистить и избежать коллизий.tempfile или $$ могут быть полезны для этого.Каналы не предназначены для буферизации данных, так как они часто имеют ограниченный размер и просто блокируют записи.

Пример, где каналы - неправильное решение:

mkfifo wcin wcout
wc -l < wcin > wcout &
yes | dd count=1 bs=8M | tee wcin | cat -n wcout - | head

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

Редактировать ответ dmckee: Да, порядок может быть повторяемым, но это не гарантируется.Это вопрос масштаба, планирования и размеров буфера.В этом окне GNU / Linux пример начинает разбиваться после нескольких тысяч строк:

seq -f line%g 20000 | tee >(awk '{print "*" $0 "*"}' ) | \
(awk '{print "this is awk: "$0}') | less
this is awk: line2397
this is awk: line2398
this is awk: line2*line1*
this is awk: *line2*
this is awk: *line3*
1 голос
/ 29 июня 2010

Я подозреваю, что в этом случае wc ожидает EOF, и поэтому он не будет возвращать (или выводить на печать), пока первая команда не выполнит отправку ввода, тогда как awk действует построчно и поэтому всегда будет печатать первым,Я не знаю, определено ли это при отправке другим процессам.

Почему бы просто не подсчитать количество строк в awk перед печатью самих строк?

0 голосов
/ 29 июня 2010

Я не думаю, что вы можете рассчитывать на это.wc здесь выполняется в отдельном процессе, поэтому синхронизация не выполняется. Мой пробный запуск предполагает, что это может быть (по крайней мере, в bash).Как Дейнит объясняет , этот конкретный случай особенный, но попробуйте его с grep -o line вместо wc и посмотрите, что вы получите.

Тем не менее, на моем MacBoox я получаю:

$ printf "line1\nline2\nline3\nline4\nline5\n" | tee >(grep -o line ) | (awk '{print "this is awk: "$0}')
this is awk: line1
this is awk: line2
this is awk: line3
this is awk: line4
this is awk: line5
this is awk: line
this is awk: line
this is awk: line
this is awk: line
this is awk: line

очень последовательно.Чтобы быть уверенным, мне нужно очень внимательно прочитать страницу руководства bash.

Аналогично:

$ printf "line1\nline2\nline3\nline4\nline5\n" | tee >(awk '{print "*" $0 "*"}' ) | (awk '{print "this is awk: "$0}')
this is awk: line1
this is awk: line2
this is awk: line3
this is awk: line4
this is awk: line5
this is awk: *line1*
this is awk: *line2*
this is awk: *line3*
this is awk: *line4*
this is awk: *line5*

каждый раз ... и

$ printf "line1\nline2\nline3\nline4\nline5\n" | tee >(awk '{print "*" $0 "*"}' ) | (grep line)
line1
line2
line3
line4
line5
*line1*
*line2*
*line3*
*line4*
*line5*
...