Краткий ответ: последствия изменения IFS
сложны и трудны для понимания, и их лучше избегать, за исключением нескольких четко определенных идиом (IFS=, read ...
- это одна из тех фраз, которые я считаю приемлемыми).
Длинный ответ: есть несколько вещей, которые вы должны иметь в виду, чтобы понять результаты, которые вы видите от изменений в IFS
:
Использование IFS=something
в качествепрефикс команды изменяется IFS
только для выполнения этой одной команды .В частности, это не влияет на то, как оболочка анализирует аргументы, передаваемые этой команде;это управляется значением оболочки IFS
, а не тем, которое используется для выполнения команды.
Некоторые команды обращают внимание на значение IFS
, с которым они выполняются (например,read
), но другие этого не делают (например, echo
).
Учитывая вышесказанное, IFS=, read -a A
делает то, что вы ожидаете, он делит свой ввод на ",":
$ IFS=, read -a A <<<"alpha,bravo,charlie"
$ declare -p A
declare -a A='([0]="alpha" [1]="bravo" [2]="charlie")'
Но echo
не обращает внимания;он всегда ставит пробелы между передаваемыми аргументами, поэтому использование IFS=something
в качестве префикса к нему никак не влияет:
$ echo alpha bravo
alpha bravo
$ IFS=, echo alpha bravo
alpha bravo
Так что, когда вы используете IFS=, echo "${A[*]:1:2}"
, это эквивалентно просто echo "${A[*]:1:2}"
и поскольку определение оболочки IFS
начинается с пробела, она помещает элементы A
вместе с пробелами между ними.Таким образом, это эквивалентно выполнению IFS=, echo "alpha bravo"
.
С другой стороны, IFS=,; echo "${A[*]:1:2}"
меняет определение оболочки на IFS
, поэтому оно влияет на то, как оболочка соединяет элементы, так что получается эквивалентноIFS=, echo "alpha,bravo"
.К сожалению, с этого момента это также влияет на все остальное, поэтому вам нужно либо изолировать его до подоболочки, либо впоследствии вернуть его в нормальное состояние.
Просто для полноты, вот пара других версий, которые этого не делаютwork:
$ IFS=,; echo "${A[@]:1:2}"
bravo charlie
В этом случае [@]
указывает оболочке обрабатывать каждый элемент массива как отдельный аргумент, поэтому для объединения их остается echo
, и он игнорирует IFS
и всегда использует пробелы.
$ IFS=,; echo "${A[@]:1:2}"
bravo charlie
Как насчет этого:
$ IFS=,; echo ${A[*]:1:2}
bravo charlie
В этом случае [*]
говорит оболочке смешивать все элементы вместе с первым символомIFS
между ними, давая bravo,charlie
.Но это не в двойных кавычках, поэтому оболочка немедленно разделяет его на «,», снова разделяя его на отдельные аргументы (а затем echo
объединяет их с пробелами, как всегда).
Если выЕсли вы хотите изменить определение оболочки на IFS
, не изолируя его от подоболочки, есть несколько вариантов, чтобы изменить его и впоследствии установить обратно.В bash вы можете вернуть его в нормальное состояние следующим образом:
$ IFS=,
$ while read -a A; do # Note: IFS change not needed here; it's already changed
> echo "${A[*]:1:2}"
> done <<<alpha,bravo,charlie
bravo,charlie
$ IFS=$' \t\n'
Но синтаксис $'...'
доступен не во всех оболочках;если вам нужна переносимость, лучше использовать буквенные символы:
IFS='
' # You can't see it, but there's a literal space and tab after the first '
Некоторые люди предпочитают использовать unset IFS
, что просто заставляет оболочку работать по умолчанию, что в значительной степени аналогично IFS
определяется обычным способом.
... но если IFS
был изменен в каком-то более крупном контексте, и вы не хотите это испортить, вам нужно сохранить его, а затем установить обратно.Если он был изменен в обычном режиме, это сработает:
saveIFS=$IFS
...
IFS=$saveIFS
... но если кто-то посчитает целесообразным использовать unset IFS
, это определит его как пустое, что даст странные результаты.Таким образом, вы можете использовать этот подход или unset
подход, но не оба.Если вы хотите сделать это устойчивым к конфликту unset
, вы можете использовать что-то вроде этого в bash:
saveIFS=${IFS:-$' \t\n'}
... или для переносимости, отключите $' '
и используйте буквальное пространство +tab + newline:
saveIFS=${IFS:-
} # Again, there's an invisible space and tab at the end of the first line
В общем, это много путаницы, полной ловушек для неосторожных.Я рекомендую избегать этого всякий раз, когда это возможно.