У некоторых людей есть ошибочное представление, что read
это команда для чтения строки. Это не.
read
читает слова из строки (возможно, с обратной косой чертой), где слова $IFS
разделяются и обратная косая черта может использоваться для экранирования (или продолжения строк).
Общий синтаксис:
read word1 word2... remaining_words
read
читает стандартный ввод один байт в то время , пока он не найдет неэкранированный символ новой строки (или конца входного текста), расщепляется , что в соответствии со сложными правилами и сохраняет результат этого расщепления $word1
, $word2
... $remaining_words
.
Например, на входе, как:
<tab> foo bar\ baz bl\ah blah\
whatever whatever
и со значением по умолчанию $IFS
, read a b c
назначит:
$a
⇐ foo
$b
⇐ bar baz
$c
⇐ blah blahwhatever whatever
Теперь, если передан только один аргумент, он не становится read line
. Это все еще read remaining_words
. Обработка обратной косой черты все еще выполняется, символы пробелов IFS по-прежнему удаляются с начала и до конца.
-r
Опция удаляет обработку обратной косой. Так что та же самая команда с выше -r
вместо назначит
$a
⇐ foo
$b
⇐ bar\
$c
⇐ baz bl\ah blah\
Теперь для части разделения важно понимать, что есть два класса символов для $IFS
: пробельные символы IFS (а именно пробел и табуляция (и новая строка, хотя здесь это не имеет значения, если вы не используете -d), что также происходит быть в значении по умолчанию $IFS
) и другие. Обработка этих двух классов персонажей различна.
С IFS=:
( :
причем не в качестве IFS символ пробела), вход как :foo::bar::
бы разделилась на ""
, "foo"
, ""
, bar
и ""
(и дополнительно ""
с некоторыми реализациями , хотя это не имеет значения , за исключением read -a
). Хотя, если мы заменим это :
пробелом, разбиение будет сделано только на foo
и bar
. То есть ведущие и конечные игнорируются, а их последовательности рассматриваются как единое целое. Существуют дополнительные правила при объединении пробельных и непробельных символов $IFS
. Некоторые реализации могут добавлять / удалять специальную обработку, удваивая символы в IFS ( IFS=::
или IFS=' '
).
Итак, здесь, если мы не хотим, чтобы начальные и конечные неэкранированные пробельные символы были удалены, нам нужно удалить эти пробельные символы IFS из IFS.
Даже с символами IFS, не являющимися пробелами, если строка ввода содержит один (и только один) из этих символов, и это последний символ в строке (как IFS=: read -r word
на входе, подобном foo:
) с оболочками POSIX (нет, zsh
ни в некоторых pdksh
версиях), этот ввод рассматривается как одно foo
слово, потому что в этих оболочках символы $IFS
рассматриваются как терминаторы , поэтому word
будут содержать foo
, а не foo:
.
Итак, канонический способ чтения одной строки ввода с помощью read
встроенной функции:
IFS= read -r line
(обратите внимание, что для большинства read
реализаций это работает только для текстовых строк, поскольку символ NUL не поддерживается, кроме как в zsh
).
Использование var=value cmd
синтаксиса гарантирует, что он IFS
будет установлен по-разному только на время выполнения этой cmd
команды.
Историческая справка
read
Встроенный был введен Bourne оболочки и уже читать слова , а не линии. Есть несколько важных отличий от современных оболочек POSIX.
Оболочка Bourne read
не поддерживает -r
опцию (которая была введена оболочкой Korn), поэтому нет способа отключить обработку обратной косой черты, кроме предварительной обработки ввода чем-то вроде sed 's/\\/&&/g'
этого.
Оболочка Bourne не имела такого понятия двух классов символов (которое снова было введено ksh). В оболочке Борна все символы пройти такое же лечение , как IFS пробельные символы делают в KSH, то есть IFS=: read a b c
на входе , как foo::bar
бы назначить bar
на $b
, а не пустую строку.
В оболочке Борна, с:
var=value cmd
Если cmd
встроенный (как read
есть), var
остается установленным value
после того, cmd
как закончил. Это особенно важно, $IFS
поскольку в оболочке Bourne $IFS
используется для разделения всего, а не только расширений. Кроме того, если вы удалите символ пробела из $IFS
оболочки Bourne, он "$@"
больше не будет работать.
В оболочке Bourne перенаправление составной команды приводит к тому, что она запускается в подоболочке (в самых ранних версиях даже такие вещи, как read var < file
или exec 3< file; read var <&3
не работают), поэтому в оболочке Bourne редко можно было использовать read
что-либо, кроме ввода пользователя на терминале (где имеет смысл обработка продолжения строки)
Некоторые Unices (например, HP / UX, есть еще один util-linux
) по-прежнему имеют line
команду для чтения одной строки ввода (которая раньше была стандартной командой UNIX вплоть до версии 2 спецификации Single UNIX ).
Это в основном то же самое, head -n 1
за исключением того, что он читает по одному байту за раз, чтобы убедиться, что он не читает более одной строки. На этих системах вы можете сделать:
line=`line`
Конечно, это означает порождение нового процесса, выполнение команды и чтение ее результатов по каналу, что намного менее эффективно, чем ksh IFS= read -r line
, но все же намного более интуитивно понятно.