Когда я могу использовать временный IFS для разделения поля?


19

В Bash, скажем, у вас есть var=a.b.c., то:

$ IFS=. printf "%s\n" $var
a.b.c

Однако такое использование IFSвступает в силу при создании массива:

$ IFS=. arr=($var)
$ printf "%s\n" "${arr[@]}"
a
b
c

Это очень удобно, конечно, но где это задокументировано? Беглое прочтение разделов, посвященных массивам или разделению слов в документации Bash, никоим образом не дает никаких указаний. Поиск IFSпо одностраничной документации также не дает никаких подсказок об этом эффекте.

Я не уверен, когда я могу надежно сделать:

IFS=x do something

И ожидать, что IFSэто повлияет на расщепление поля.

Ответы:


28

Основная идея заключается в том, что VAR=VALUE some-commandнаборы VARдля VALUEдля исполнения , some-commandкогда some-commandесть внешняя команда, и она не получает больше фантазии , чем это. Если вы объедините эту интуицию с некоторыми знаниями о том, как работает оболочка, вы должны придумать правильный ответ в большинстве случаев. Ссылка на POSIX - «Простые команды» в главе «Язык команд оболочки» .

Если some-commandэто внешняя команда , VAR=VALUE some-commandэквивалентно env VAR=VALUE some-command. VARэкспортируется в среде some-command, и его значение (или отсутствие значения) в оболочке не изменяется.

Если some-commandэто функция , то VAR=VALUE some-commandэто эквивалентно VAR=VALUE; some-command, т.е. назначение остается на месте после того, как функция вернулась, и переменная не экспортируется в среду. Причина этого связана с разработкой оболочки Bourne (и, следовательно, с обратной совместимостью): у нее не было возможности сохранять и восстанавливать значения переменных при выполнении функции. Не экспортировать переменную имеет смысл, так как функция выполняется в самой оболочке. Однако ksh (включая ATT ksh93 и pdksh / mksh), bash и zsh реализуют более полезное поведение, VARкоторое задается только во время выполнения функции (также экспортируется). В ksh это делается, если функция определена с синтаксисом kshfunction NAME …не, если это определено со стандартным синтаксисом NAME (). В bash это делается только в режиме bash, а не в режиме POSIX (при запуске с POSIXLY_CORRECT=1). В zsh это делается, если posix_builtinsопция не установлена; эта опция не установлена ​​по умолчанию, но включена emulate shили emulate ksh.

Если some-commandявляется встроенным, поведение зависит от типа встроенного. Специальные встроенные функции ведут себя как функции. Специальные встроенные модули - это те, которые должны быть реализованы внутри оболочки, потому что они влияют на оболочку состояния (например, breakвлияют на поток управления, cdвлияют на текущий каталог, setвлияют на позиционные параметры и опции…). Другие встроенные функции встроены только для повышения производительности и удобства (в основном - например, функция bash printf -vможет быть реализована только встроенной функцией ), и они ведут себя как внешняя команда.

Назначение происходит после раскрытия псевдонима, поэтому, если some-commandэто псевдоним , сначала разверните его, чтобы выяснить, что происходит.

Обратите внимание, что во всех случаях назначение выполняется после анализа командной строки, включая любые подстановки переменных в самой командной строке. Таким образом, var=a; var=b echo $varпечатает a, потому что $varоценивается до того, как назначение имеет место. И таким образом IFS=. printf "%s\n" $varиспользует старое IFSзначение для разделения $var.

Я рассмотрел все типы команд, но есть еще один случай: когда нет команды для выполнения , т.е. если команда состоит только из назначений (и, возможно, перенаправлений). В этом случае назначение остается на месте . VAR=VALUE OTHERVAR=OTHERVALUEэквивалентно VAR=VALUE; OTHERVAR=OTHERVALUE. Таким образом , после того IFS=. arr=($var), IFSостается равным .. Поскольку вы можете использовать $IFSв присваивании arrс ожиданием того, что оно уже имеет новое значение, имеет смысл использовать новое значение IFSдля расширения $var.

Таким образом, вы можете использовать только IFSдля временного разделения поля:

  • путем запуска новой оболочки или подоболочки (например third=$(IFS=.; set -f; set -- $var; echo "$3"), это сложный способ, за third=${var#*.*.}исключением того, что они ведут себя по-разному, если значение varсодержит менее двух .символов);
  • в ksh, IFS=. some-functionгде some-functionопределяется с помощью синтаксиса ksh function some-function …;
  • в bash и zsh, при IFS=. some-functionусловии, что они работают в основном режиме, а не в режиме совместимости.

" IFSостается установленным ." Eek. После прочтения первой части это имеет смысл, но до того, как я разместил этот вопрос, я бы этого не ожидал.
Муру

1
Это ответ на другой вопрос
schily

Некоторое дополнительное объяснение в этом ответе несколько лет назад .
Ти Стрга

6

Ответ @Gilles действительно велик, он объясняет (подробно) сложную проблему.

Тем не менее, я считаю, что ответ на эту команду:

$ IFS=. printf "%s\n" $var
a.b.c

работает так, как работает, это простая идея, что вся командная строка анализируется перед ее выполнением. И что каждое «слово» обрабатывается оболочкой один раз.
Эти задания, как IFS=., задерживаются (шаг 4 является последним):

4.- Каждое назначение переменной должно быть расширено ...

до тех пор, пока команда не будет выполнена и все расширения в аргументах не будут обработаны в первую очередь для построения этой исполняемой строки:

$ IFS=. printf "%s\n" a.b.c           ## IFS=. goes to the environment.
a.b.c

Значение $varрасширяется с помощью «старого» IFS до a.b.cтого, как команде printfбудут даны аргументы "%s\n"и a.b.c.

Eval

Один уровень задержки может быть введен eval:

$ IFS=. eval printf "'%s\n'" \$var
a
b
c

Строка анализируется (1-й раз) и 'IFS =.' устанавливается для окружающей среды, как это:

$ printf '%s\n' $var

Затем он снова анализируется:

$ printf '%s\n' a b c

И выполнил к этому:

a
b
c

Значение $var(ABC) делится со значением КСФ в использовании: ..

Окружающая обстановка

Сложная и хитрая часть - то, что действительно в окружающей среде, когда !!!

Это очень хорошо объясняется в первой части ответа Жиля.

С дополнительной деталью.

Когда эта команда выполнена:

$ IFS=. arr=($var)

Значение IFS сохраняется в нынешних условиях, да:

$ printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  <.> 

IFS для одного заявления.

Но этого можно избежать: настройка IFS для одного оператора

$ IFS=. command eval arr\=\(\$var\)

$  printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  < 
> 

2

Ваш вопрос относительно

var=a.b.c
IFS=. printf "%s\n" $var

это угловой случай.

Это потому, что команда macro expansionin происходит до того, IFS=.как установлена переменная оболочки .

Другими словами: когда $varраскрыто, предыдущее IFSзначение активно, затем IFSустанавливается на '.'.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.