Следующие несколько тем на этом сайте и StackOverflow были полезны для понимания того, как IFS
работает:
Но у меня все еще есть несколько коротких вопросов. Я решил спросить их в том же посте, так как думаю, что это может помочь лучшим будущим читателям:
Q1. IFS
обычно обсуждается в контексте «разделения поля». Является ли разделение полей так же , как слово расщепления ?
Q2: спецификация POSIX гласит :
Если значение IFS равно нулю, разделение полей не производится.
Установка IFS=
совпадает с установкой IFS
на ноль? Это то, что подразумевается под установкой его empty string
тоже?
Q3: В спецификации POSIX я прочитал следующее:
Если IFS не установлен, оболочка должна вести себя так, как если бы значение IFS было
<space>, <tab> and <newline>
Скажем, я хочу восстановить значение по умолчанию IFS
. Как мне это сделать? (более конкретно, как я имею в виду <tab>
и <newline>
?)
Q4: наконец, как бы этот код:
while IFS= read -r line
do
echo $line
done < /path_to_text_file
вести себя, если мы изменим первую строку на
while read -r line # Use the default IFS value
или:
while IFS=' ' read -r line
IFS
и неустановленноеIFS
значения очень разные. Ответ на вопрос 4 отчасти неправильный: внутренние разделители здесь не затрагиваются, только ведущие и конечные.IFS
, все они имеют в видуIFS=
.IFS=' ' ; foo=( bar baz qux ) ; echo "${#foo[@]}"
. (Э-э, что? Там должно быть несколько разделителей пробела, так что движок SO продолжает их удалять).read
; последняя переменная захватывает все, что осталось, кроме последнего разделителя, и оставляет внутренние разделители внутри.Q1: да. «Разделение полей» и «разделение слов» являются двумя терминами одного и того же понятия.
Q2: да. Если
IFS
не установлено (то есть послеunset IFS
), это эквивалентноIFS
установке$' \t\n'
(пробел, табуляция и перевод строки). ЕслиIFS
установлено пустое значение (это то, что здесь означает «ноль») (т. Е. ПослеIFS=
илиIFS=''
илиIFS=""
), разделение полей вообще не выполняется (и$*
, как правило, использует первый символ$IFS
, использует пробел).Q3: Если вы хотите иметь
IFS
поведение по умолчанию , вы можете использоватьunset IFS
. Если вы хотитеIFS
явно установить это значение по умолчанию, вы можете поместить пространство буквенных символов, табуляцию, символ новой строки в одинарные кавычки. В ksh93, bash или zsh вы можете использоватьIFS=$' \t\n'
. В частности, если вы хотите избежать буквального символа табуляции в исходном файле, вы можете использоватьQ4: с
IFS
установленным на пустое значение,read -r line
устанавливаетline
на всю строку, кроме завершающей новой строки. С помощьюIFS=" "
пробелов в начале и конце строки обрезаются. При значении по умолчаниюIFS
вкладки и пробелы обрезаются.источник
$@
в контекстах, не включенных в список, есть некоторые различия между оболочкамиIFS=; var=$@
). Следует отметить, что когда IFS пуст, разделение слов не выполняется, но $ var по-прежнему расширяется до без аргумента вместо пустого аргумента, когда $ var пуст и применяется глобализация, поэтому вам все равно нужно заключать в кавычки переменные (даже если вы отключить сглаживание)Q1. Расщепление поля.
Да, оба указывают на одну и ту же идею.
Q2: Когда IFS является нулевым ?
Да, все три означают одно и то же: разделение полей / слов не должно выполняться. Кроме того, это влияет на поля печати (как в случае
echo "$*"
), все поля будут объединены вместе без пробелов.Q3: (часть а) Отключить IFS.
Что в точности эквивалентно:
Это означает, что «Разделение поля» будет точно таким же, как и значение IFS по умолчанию, или не будет установлено.
Это НЕ означает, что IFS будет работать одинаково в любых условиях. Чтобы быть более точным, выполнение
OldIFS=$IFS
установит для переменнойOldIFS
значение null , а не по умолчанию. И попытка установить IFS обратно, как это,IFS=OldIFS
установит для IFS значение null, а не оставить его неустановленным, как это было раньше. Осторожно !!.Q3: (часть b) Восстановить IFS.
Для zsh, ksh и bash (AFAIK) для IFS может быть установлено значение по умолчанию:
Готово, больше ничего не нужно читать.
Но если вам нужно переустановить IFS для sh, это может стать сложным.
Давайте посмотрим с самого простого на комплект без недостатков (кроме сложности).
1.- Отключить IFS.
Мы могли бы просто
unset IFS
(Прочтите Q3 часть а выше).2.- Поменять местами символы.
В качестве обходного пути, если поменять значения табуляции и новой строки, проще установить значение IFS, а затем он работает аналогичным образом.
Установите IFS на <пробел> <новая строка> <вкладка> :
3.- Простой? решение:
Если есть дочерние сценарии, для которых требуется правильно установить IFS, вы всегда можете написать вручную:
Где последовательность, набранная вручную, была:,
IFS=
'spacetabnewline'последовательность, которая на самом деле была правильно набрана выше (Если вам нужно подтвердить, отредактируйте этот ответ). Но копирование / вставка из вашего браузера сломается, потому что браузер будет сжимать / скрывать пробелы. Это затрудняет совместное использование кода, как написано выше.4.- Полное решение.
Для написания кода, который можно безопасно скопировать, обычно требуется однозначный выход для печати.
Нам нужен код, который «производит» ожидаемое значение. Но, даже если концептуально правильно, этот код НЕ будет устанавливать трейлинг
\n
:Это происходит потому, что в большинстве оболочек все завершающие символы новой строки
$(...)
или`...`
замены команд удаляются при расширении.Нам нужно использовать трюк для sh:
Альтернативный способ может состоять в том, чтобы установить IFS в качестве значения среды из bash (например), а затем вызвать sh (версии, которые принимают IFS для установки через среду), как это:
Короче говоря, sh делает сброс IFS по умолчанию довольно странным приключением.
Q4: в фактическом коде:
Во-первых: я не знаю, есть ли
echo $line
(с указанием var NOT) на porpouse или нет. Он вводит второй уровень «разделения поля», который не имеет чтения. Поэтому я отвечу на оба. :)С этим кодом (чтобы вы могли подтвердить). Вам понадобится полезный xxd :
Я получил:
Первое значение - это только правильное значение
IFS=
'spacetabnewline'Следующая строка - это все шестнадцатеричные значения, которые
$a
имеет переменная var , и новая строка '0a' в конце, которая будет передана каждой команде чтения.Следующая строка, для которой IFS имеет значение null, не выполняет никакого «разделения поля», но новая строка удаляется (как и ожидалось).
Следующие три строки, поскольку IFS содержит пробел, удаляют начальные пробелы и устанавливают в строке var оставшееся сальдо.
Последние четыре строки показывают, что будет делать переменная без кавычек. Значения будут разделены на (несколько) пробелов и будут напечатаны как:
bar,baz,qux,
источник
unset IFS
очищает IFS, даже если впоследствии предполагается, что IFS равен "\ t \ n":Протестировано на bash версий 4.2.45 и 3.2.25 с таким же поведением.
источник
unset
оIFS
, как объяснено в комментариях принятого ответа здесь.