Понимание командной строки для чтения - PullRequest
0 голосов
/ 03 марта 2019

Я пытаюсь понять оболочку UNIX, и команда "read" озадачивает меня.Как показывает следующий фрагмент (или идиома «while-read»), эта команда «съедает» строку стандартного ввода.

(read -r foo ; echo '*** Before cat ***' ; cat) << 'END'
hello
world
END

Выводы: *** Before cat *** world У меня следующие вопросы:

  1. Кто-нибудь знает, что другие команды имеют такое же поведение, то есть используют только одну строку стандартного ввода, а не весь ввод?
  2. Можно ли написать команду без оболочки?читать, с похожим поведением?

1 Ответ

0 голосов
/ 07 марта 2019

Поведение по умолчанию

Хотя read надежно считывает только одну строку за раз, давайте посмотрим, сколько потребляют три другие общие команды.head -1 печатает только первую строку.Однако для этого он потребляет 8192 байта из стандартного ввода:

$ seq 5000 | tee >(wc >/dev/tty) | ( head -1 >/dev/null; cat) | wc
   5000    5000   23893
   3141    3140   15701
$ echo $((23893 - 15701))
8192

Команда awk '{exit}' должна прочитать первую строку и выйти.Для этого, однако, он потребляет 4096 байтов стандартного ввода:

$ seq 5000 | tee >(wc >/dev/tty) | ( awk '{exit}' >/dev/null; cat) | wc
   5000    5000   23893
   3960    3960   19797
$ echo $((23893 - 19797))
4096

sed 1q должен завершиться при чтении первой строки, но также потребляет 4096 байтов стандартного ввода:

$ seq 5000 | tee >(wc >/dev/tty) | ( sed 1q >/dev/null; cat) | wc
   5000    5000   23893
   3960    3960   19797
$ echo $((23893 - 19797))
4096

Величины 8192 и 4096 являются размерами буфера в текущем коде.Можно ожидать, что эти размеры могут измениться от одной версии к следующей версии.

Использование stdbuf

stdbuf - это утилита, которая пытается контролировать размеры буфера, которые использует команда.stdbuf -i1 пытается установить размер буфера на входе в 1 байт.При применении stdbuf sed 1q использует только одну строку:

$ (echo First; echo Second) | ( stdbuf -i1 sed 1q>/dev/null; cat)
Second

При seq в качестве входных данных он по-прежнему использует только одну строку:

$ seq 5000 | tee >(wc >/dev/tty) | ( stdbuf -i1 sed 1q >/dev/null; cat) | wc
   5000    5000   23893
   4999    4999   23891

Этот же прием не работаетдля head или awk.Оба они потребляют несколько кБ, несмотря на stdbuf:

$ seq 5000 | tee >(wc >/dev/tty) | ( stdbuf -i1 head -1 >/dev/null; cat) | wc
   5000    5000   23893
   3141    3140   15701
$ echo $((23893 - 15701))
8192
$ seq 5000 | tee >(wc >/dev/tty) | ( stdbuf -i1 awk '{exit}' >/dev/null; cat) | wc
   5000    5000   23893
   3960    3960   19797
$ echo $((23893 - 19797))
4096

Сводка

В то время как встроенная оболочка read надежно потребляет только одну строку за раз, внешние команды, такие как sed, awk и head потребляют зависящее от реализации количество стандартного ввода даже при обработке одной строки.

Чтобы ответить на два вопроса:

  1. В текущей реализации возможно использовать только одну строку stdbuf, используя stdbuf -i1 sed 1q, но нет никаких гарантий, что это продолжит работать в любой будущей версии.

  2. пример stdbuf -i1 sed 1q указывает, что можно написать утилиту, которая отключит входную буферизацию и, следовательно, потребляет только столько стандартного ввода, сколько необходимо.

...