Q1. Расщепление поля.
Разделение полей - это то же самое, что и разделение слов?
Да, оба указывают на одну и ту же идею.
Q2: Когда IFS является нулевым ?
Это IFS=''
то же самое, что и пустая строка?
Да, все три означают одно и то же: разделение полей / слов не должно выполняться. Кроме того, это влияет на поля печати (как в случае echo "$*"
), все поля будут объединены вместе без пробелов.
Q3: (часть а) Отключить IFS.
В спецификации POSIX я прочитал следующее :
Если IFS не установлен, оболочка должна вести себя так, как если бы значение IFS было <space> <tab> <newline> .
Что в точности эквивалентно:
При unset IFS
этом оболочка должна вести себя так, как будто IFS используется по умолчанию.
Это означает, что «Разделение поля» будет точно таким же, как и значение IFS по умолчанию, или не будет установлено.
Это НЕ означает, что IFS будет работать одинаково в любых условиях. Чтобы быть более точным, выполнение OldIFS=$IFS
установит для переменной OldIFS
значение null , а не по умолчанию. И попытка установить IFS обратно, как это, IFS=OldIFS
установит для IFS значение null, а не оставить его неустановленным, как это было раньше. Осторожно !!.
Q3: (часть b) Восстановить IFS.
Как я могу восстановить значение IFS по умолчанию. Скажем, я хочу восстановить значение по умолчанию IFS. Как мне это сделать? (более конкретно, как я могу обратиться к <tab> и <newline> ?)
Для zsh, ksh и bash (AFAIK) для IFS может быть установлено значение по умолчанию:
IFS=$' \t\n' # works with zsh, ksh, bash.
Готово, больше ничего не нужно читать.
Но если вам нужно переустановить IFS для sh, это может стать сложным.
Давайте посмотрим с самого простого на комплект без недостатков (кроме сложности).
1.- Отключить IFS.
Мы могли бы просто unset IFS
(Прочтите Q3 часть а выше).
2.- Поменять местами символы.
В качестве обходного пути, если поменять значения табуляции и новой строки, проще установить значение IFS, а затем он работает аналогичным образом.
Установите IFS на <пробел> <новая строка> <вкладка> :
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.- Простой? решение:
Если есть дочерние сценарии, для которых требуется правильно установить IFS, вы всегда можете написать вручную:
IFS =»
'
Где последовательность, набранная вручную, была:, IFS=
'spacetabnewline'последовательность, которая на самом деле была правильно набрана выше (Если вам нужно подтвердить, отредактируйте этот ответ). Но копирование / вставка из вашего браузера сломается, потому что браузер будет сжимать / скрывать пробелы. Это затрудняет совместное использование кода, как написано выше.
4.- Полное решение.
Для написания кода, который можно безопасно скопировать, обычно требуется однозначный выход для печати.
Нам нужен код, который «производит» ожидаемое значение. Но, даже если концептуально правильно, этот код НЕ будет устанавливать трейлинг \n
:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
Это происходит потому, что в большинстве оболочек все завершающие символы новой строки $(...)
или `...`
замены команд удаляются при расширении.
Нам нужно использовать трюк для sh:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
Альтернативный способ может состоять в том, чтобы установить IFS в качестве значения среды из bash (например), а затем вызвать sh (версии, которые принимают IFS для установки через среду), как это:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
Короче говоря, sh делает сброс IFS по умолчанию довольно странным приключением.
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
Во-первых: я не знаю, есть ли echo $line
(с указанием var NOT) на porpouse или нет. Он вводит второй уровень «разделения поля», который не имеет чтения. Поэтому я отвечу на оба. :)
С этим кодом (чтобы вы могли подтвердить). Вам понадобится полезный xxd :
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
Я получил:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
Первое значение - это только правильное значение IFS=
'spacetabnewline'
Следующая строка - это все шестнадцатеричные значения, которые $a
имеет переменная var , и новая строка '0a' в конце, которая будет передана каждой команде чтения.
Следующая строка, для которой IFS имеет значение null, не выполняет никакого «разделения поля», но новая строка удаляется (как и ожидалось).
Следующие три строки, поскольку IFS содержит пробел, удаляют начальные пробелы и устанавливают в строке var оставшееся сальдо.
Последние четыре строки показывают, что будет делать переменная без кавычек. Значения будут разделены на (несколько) пробелов и будут напечатаны как:bar,baz,qux,
IFS
и неустановленноеIFS
значения очень разные. Ответ на вопрос 4 отчасти неправильный: внутренние разделители здесь не затрагиваются, только ведущие и конечные.