Плохая голова с перенаправлением

В ответ на перенос файла через хвост и голову через тройник было замечено странное поведение head в следующей конструкции при работе с огромными файлами:

#! /bin/bash
for i in {1..1000000} ; do echo $i ; done > /tmp/n

( tee >(sed -n '1,3p'        >&3 ) < /tmp/n | tail -n2 ) 3>&1 # Correct
echo '#'
( tee >(tac | tail -n3 | tac >&3 ) < /tmp/n | tail -n2 ) 3>&1 # Correct
echo '#'
( tee >(head -n3             >&3 ) < /tmp/n | tail -n2 ) 3>&1 # Not correct!?

Выход:

1
2
3
999999
1000000
#
1
2
3
999999
1000000
#
1
2
3
15504
15

Вопрос:

Почему последняя строка не выводит те же строки, что и две предыдущие?


person choroba    schedule 21.05.2013    source источник


Ответы (1)


Это связано с тем, что head завершает работу, как только передает три первые строки. Впоследствии tee уничтожается с помощью SIGPIPE, потому что конец чтения канала «FILE», в который он пишет, закрыт, но не раньше, чем ему удастся вывести несколько строк на свой стандартный вывод.

Если вы выполните только это:

tee >(head -n3 >/dev/null) < /tmp/n

Вы увидите, что происходит лучше.

OTOH, tac читает весь файл, поскольку он должен перевернуть его, как и sed, вероятно, для согласованности.

person spbnick    schedule 21.05.2013
comment
Спасибо. Теперь я понимаю. Я даже могу добавить cat вот так ( tee >(head -n3 >&3; cat > /dev/null ) < /tmp/n | tail -n2 ) 3>&1, чтобы все заработало. - person choroba; 21.05.2013
comment
Не за что :) Хотя я бы сказал, что использование sed для этой части было бы понятнее. - person spbnick; 21.05.2013
comment
Обратите внимание, что для файлов меньше 5 строк, по крайней мере, некоторые из них будут выведены дважды. - person spbnick; 21.05.2013