У некоторых людей есть ошибочное представление, что 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, но все же намного более интуитивно понятно.