Ожидается, что следующая команда оболочки выведет только нечетные строки входного потока:
echo -e "aaa\nbbb\nccc\nddd\n" | (while true; do head -n 1; head -n 1 >/dev/null; done)
Но вместо этого он просто печатает первую строку: aaa
.
То же самое не происходит, когда он используется с опцией -c
( --bytes
):
echo 12345678901234567890 | (while true; do head -c 5; head -c 5 >/dev/null; done)
Эта команда выводит 1234512345
как ожидалось. Но это работает только в реализации утилиты coreutilshead
. Реализация busybox по- прежнему потребляет лишние символы, поэтому вывод просто 12345
.
Я предполагаю, что этот конкретный способ реализации сделан в целях оптимизации. Вы не можете знать, где заканчивается строка, поэтому вы не знаете, сколько символов вам нужно прочитать. Единственный способ не использовать лишние символы из входного потока - это читать поток побайтно. Но чтение из потока по одному байту за раз может быть медленным. Поэтому я предполагаю, что head
считывает входной поток в достаточно большой буфер и затем считает строки в этом буфере.
Чего нельзя сказать о случае, когда --bytes
используется опция. В этом случае вы знаете, сколько байтов вам нужно прочитать. Таким образом, вы можете прочитать именно это количество байтов и не более того. Реализация corelibs использует эту возможность, но не с busybox , она все же считывает больше байта, чем требуется, в буфер. Вероятно, это сделано для упрощения реализации.
Итак, вопрос. Правильно ли для head
утилиты потреблять больше символов из входного потока, чем было задано? Есть ли какой-то стандарт для утилит Unix? И если есть, то указывает ли это поведение?
PS
Вы должны нажать, Ctrl+C
чтобы остановить команды выше. Утилиты Unix не перестают читать дальше EOF
. Если вы не хотите нажимать, вы можете использовать более сложную команду:
echo 12345678901234567890 | (while true; do head -c 5; head -c 5 | [ `wc -c` -eq 0 ] && break >/dev/null; done)
который я не использовал для простоты.
источник
Ответы:
Да, это разрешено (см. Ниже).
Да, POSIX, том 3, Shell & Utilities .
В своем введении он делает:
head
является одной из стандартных утилит , поэтому реализация, соответствующая POSIX, должна реализовывать поведение, описанное выше.GNU
head
делает пытаться оставить дескриптор файла в правильном положении, но это невозможно искать на трубах, поэтому в тесте он не может восстановить положение. Вы можете увидеть это используяstrace
:read
возвращает 17 байт (все доступные входные),head
обрабатывает четыре из них , а затем пытается вернуться 13 байт, но она не может. (Вы также можете увидеть здесь, что GNUhead
использует буфер 8 КиБ.)Когда вы говорите
head
считать байты (что является нестандартным), он знает, сколько байтов нужно прочитать, поэтому он может (если реализован таким образом) соответствующим образом ограничить свое чтение. Вот почему вашhead -c 5
тест работает: GNUhead
читает только пять байтов и поэтому не нуждается в поиске для восстановления позиции файлового дескриптора.Если вы записываете документ в файл и используете его вместо этого, вы получите поведение, которое вам нужно:
источник
line
(теперь удаленные из POSIX / XPG, но все еще доступные во многих системах) илиread
(IFS= read -r line
), которые читают по одному байту за раз, чтобы избежать проблемы.head -c 5
чтение 5 байтов или полный буфер зависит от реализации (также обратите внимание, чтоhead -c
это не стандарт), вы не можете полагаться на это. Вы должныdd bs=1 count=5
иметь гарантию, что будет прочитано не более 5 байтов.-c 5
описание.head
встроенная функцияksh93
читает по одному байту за раз,head -n 1
когда ввод не доступен для поиска.dd
корректно работает только с каналами,bs=1
если вы используетеcount
чтение as на каналах as, которое может вернуть меньше запрошенного (но по крайней мере один байт, если eof не достигнут). У GNUdd
есть это,iflag=fullblock
что может смягчить это все же.из POSIX
Это ничего не говорит о том, сколько
head
нужно прочитать из ввода. Требовать, чтобы он читал побайтово, было бы глупо, поскольку это было бы чрезвычайно медленно в большинстве случаев.Это, однако, решено во
read
встроенной / служебной программе: все оболочки, которые я могу найтиread
из каналов, по одному байту за раз, и стандартный текст можно интерпретировать как означающее, что это должно быть сделано, чтобы можно было прочитать только эту единственную строку:В случае
read
, который используется в сценариях оболочки, общий вариант использования будет выглядеть примерно так:Здесь стандартный ввод для ввода
someprogram
такой же, как и для оболочки, но можно ожидать, что онsomeprogram
будет читать все, что происходит после первой строки ввода, потребляемой,read
а не то, что осталось после буферизованного чтенияread
. С другой стороны, использование,head
как в вашем примере, гораздо более редко.Если вы действительно хотите удалить все остальные строки, было бы лучше (и быстрее) использовать какой-либо инструмент, который может обрабатывать весь ввод за один раз, например
источник
-r
,read
может прочитать более одной строки (безIFS=
него также будут лишены начальные и конечные пробелы и табуляции (со значением по умолчанию$IFS
)).head
встроенная функцияksh93
читает по одному байту за раз,head -n 1
когда ввод не доступен для поиска.источник