Голова потребляет дополнительные символы из stdin?

Когда я выполняю следующую команду head:

yes 123456789 | ( head -n 1; head -n 1 )

Я получаю:

123456789
3456789

Пока я ожидал:

123456789
123456789

Это также озадачивает меня, когда я выполняю:

echo -e "123456789\n123456789\n123456789\n123456789\n123456789\n" | \
( head -n 1; head -n 1 )

Я получаю:

123456789

вместо:

123456789
123456789

Я думаю, есть что-то, чего я не понимаю. Вы знаете, почему я получаю такое поведение?

Ответ 1

Ввод и вывод - совершенно разные звери. Руководство пользователя head сообщит вам, каков ожидаемый результат, но он ничего не говорит о том, как обрабатывается вход.

Итак, короткий ответ: вы полагаетесь на недокументированные вещи.

Теперь, если вам интересно узнать, что происходит за кулисами, вы можете добавить трассировку

| ( strace head -n 1; tail )

в вашем втором примере: Примечание: извините за формат strace, я сейчас на cygwin.:

[...]
 24   35374 [main] head 1784 read: 51 = read(0, 0x22C700, 1024)

первый head процесс пытается прочитать ввод, читая большой фрагмент (1024 байта), а затем, вероятно, ищет символ новой строки в буфере. По крайней мере, так, как бы я его реализовал. Как вы можете видеть, он обработал все 51 символ, поэтому ничего не осталось для следующего процесса.

в вашем первом примере: основное отличие здесь в том, что у нас есть бесконечный вход, поэтому, даже если первый head будет читать большой кусок, там также будет введен слева для второго процесса. Граница будет произвольной, она зависит от размера блока, реализации главы, как реализуется fread (буферизованный IO) и так далее. Например, в моей системе это был результат:

123456789
56789

Ответ 2

Да, head определенно читает более одной строки. Он будет выполнять буферизованный ввод-вывод. Чтение из файла, кажется, читается строками, но из канала он считывает что-то вроде 512 байтов за раз. Это будет соответствовать тому, что вы видите. 3456789, вероятно, не вторая строка, а 52-й. Чтобы поэкспериментировать с этим, используйте что-то, где вы можете разделить строки, а не yes. cat somefile | работает красиво.

Ответ 3

(Поздний ответ здесь.)

Пока существующий ответ объясняет причину того, что вы наблюдаете, вы можете использовать обходной путь для получения ожидаемого результата.

Подключите вывод к тому, что будет выводить вывод строки:

$ yes 123456789 | { head -n 1; head -n 1; }
123456789
56789
$ yes 123456789 | grep --line-buffered . | { head -n 1; head -n 1; }
123456789
123456789

Заметьте, что я использовал { ... }, т.е. группировку команд, которая в отличие от ( ... ) не создает подоболочку.

Ответ 4

Если вы хотите получить

123456789
123456789

тогда вам нужно что-то вроде этого:

yes 123456789 | head -2

(да, петля до разрыва трубы, голова -2 дает вам 2 строки)

И для второй части это должно быть следующим, чтобы получить то, что вы хотите:)

echo -e "123456789\n123456789\n123456789\n123456789\n123456789\n" | head -2