Я, очевидно, понимаю, что можно добавить значение к внутренней переменной разделителя полей. Например:
$ IFS=blah
$ echo "$IFS"
blah
$
Я также понимаю, что read -r line
будет сохранять данные из stdin
переменной с именем line
:
$ read -r line <<< blah
$ echo "$line"
blah
$
Однако, как команда может назначить значение переменной? И делает он сначала сохранить данные stdin
в переменной , line
а затем дать значение line
для IFS
?
bash
shell-script
Мартин
источник
источник
Ответы:
У некоторых людей есть ошибочное представление, что
read
это команда для чтения строки. Это не.read
читает слова из строки (возможно, с обратной косой чертой), где слова$IFS
разделяются и обратная косая черта может использоваться для экранирования (или продолжения строк).Общий синтаксис:
read
читает стандартный ввод один байт в то время , пока он не найдет неэкранированный символ новой строки (или конца входного текста), расщепляется , что в соответствии со сложными правилами и сохраняет результат этого расщепления$word1
,$word2
...$remaining_words
.Например, на входе, как:
и со значением по умолчанию
$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
встроенной функции:(обратите внимание, что для большинства
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
, а не пустую строку.В оболочке Борна, с:
Если
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
за исключением того, что он читает по одному байту за раз, чтобы убедиться, что он не читает более одной строки. На этих системах вы можете сделать:Конечно, это означает порождение нового процесса, выполнение команды и чтение ее результатов по каналу, что намного менее эффективно, чем ksh
IFS= read -r line
, но все же намного более интуитивно понятно.источник
sh
различиями также полезно для написания переносимых скриптов!)bash-4.4.19
,while read -r; do echo "'$REPLY'"; done
работает какwhile IFS= read -r line; do echo "'$line'"; done
.read
чтения строки является ошибочным, должно быть что-то еще. Что это за ошибочное понятие? Или это первое утверждение технически правильно, но на самом деле ошибочное понятие таково: «чтение - это команда для чтения слов из строки. Поскольку она настолько мощная, вы можете использовать ее для чтения строк из файла, выполнив:IFS= read -r line
»Теория
Здесь действуют две концепции:
IFS
является разделителем поля ввода, что означает, что прочитанная строка будет разделена на основе символов вIFS
. В командной строкеIFS
обычно используются любые пробельные символы, поэтому командная строка разделяется пробелами.VAR=value command
означает «изменить окружение команды так,VAR
чтобы оно имело значениеvalue
». По сути, командаcommand
будетVAR
иметь значениеvalue
, имеющее значение , но любая команда, выполненная после этого, все равно будетVAR
иметь свое предыдущее значение. Другими словами, эта переменная будет изменена только для этого оператора.В этом случае
Таким образом, при выполнении
IFS= read -r line
задания вы устанавливаетеIFS
пустую строку (для разделения не будет использоваться ни один символ, поэтому разделения не произойдет), чтобыread
прочитать всю строку и увидеть ее как одно слово, которое будет присвоеноline
переменной. ИзмененияIFS
влияют только на этот оператор, поэтому изменение не повлияет на следующие команды.Как примечание стороны
В то время как команда правильно и будет работать , как задумано, установка
IFS
в данном случаенеможет 1 не быть необходимым. Как написано наbash
странице руководства воread
встроенном разделе:Поскольку у вас есть только
line
переменная, ей будут присвоены все слова, так что если вам не нужны никакие из предшествующих и завершающих пробельных символов 1, вы можете просто написатьread -r line
и покончить с этим.[1] Так же, как пример того, как значение
unset
или$IFS
значение по умолчанию заставитread
рассматривать начальные / конечные пробелы IFS , вы можете попробовать:Запустите его, и вы увидите, что предшествующие и конечные символы не выживут, если
IFS
не установлены. Кроме того, некоторые странные вещи могут произойти, если$IFS
в сценарии нужно что-то изменить ранее.источник
Вы должны прочитать это заявление в двух частях, первая очищает значение переменной IFS, т.е. эквивалентно более читаемым
IFS=""
, второй читаетline
переменную из стандартного ввода,read -r line
.Что характерно в этом синтаксисе, так это то, что влияние IFS является временным и действует только для
read
команды.Если я что-то не упустил, в этом конкретном случае очистка неIFS
имеет никакого эффекта, хотя, как бы то ниIFS
было, вся строка будет считана вline
переменной. Поведение могло бы измениться только в том случае, если в качестве параметраread
инструкции было передано более одной переменной .Редактировать:
Он
-r
предназначен для того, чтобы ввод, заканчивающийся на,\
не обрабатывался специально, т. Е. Для включения обратной косой черты вline
переменную, а не в качестве символа продолжения, чтобы разрешить многострочный ввод.Очистка IFS имеет побочный эффект предотвращения чтения, чтобы обрезать потенциальные начальные и конечные пробелы или символы табуляции, например:
Спасибо Ричи за то, что указал на эту разницу.
источник
read -r line
он обрежет начальные и конечные пробелы, прежде чем назначить входные данные дляline
переменной.IFS= read a b <<< 'aa bb' ; echo "-$a-$b-"
покажет-aa bb--