Подтвердить что ты не робот

IFS =: приводит к разному поведению при циклировании значений, разделенных двоеточиями, в разных случаях

Эксперимент 1

Вот мой первый script в файле с именем foo.sh.

IFS=:
for i in foo:bar:baz
do
    echo $i
done

Здесь выдается следующий вывод.

$ bash foo.sh
foo bar baz

Эксперимент 2

Это мой второй script.

IFS=:
for i in foo:bar:baz
do
    unset IFS
    echo $i
done

Здесь выдается следующий вывод.

$ bash foo.sh
foo:bar:baz

Эксперимент 3

Это мой третий script.

IFS=:
var=foo:bar:baz
for i in $var
do
    echo $i
done

Здесь выдается следующий вывод.

$ bash foo.sh
foo
bar
baz

Почему результат во всех трех случаях отличается? Можете ли вы объяснить правила интерпретации IFS и команды, которые приводят к это разные выходы?

4b9b3361

Ответ 1

Я нашел этот очень интересный эксперимент. Спасибо за это.


Чтобы понять, что происходит, соответствующий раздел из man bash таков:

  Word Splitting
      The  shell  scans the results of parameter expansion, command substitu-
      tion, and arithmetic expansion that did not occur within double  quotes
      for word splitting.

Ключом является "результат...", и он очень тонкий. То есть разбиение слов происходит на результате определенных операций, как указано там: результат расширения параметра, результат подстановки команд и т.д. Разделение слов не выполняется на строковых литералах, таких как foo:bar:baz.

Посмотрите, как эта логика воспроизводится в контексте ваших примеров.

Эксперимент 1

IFS=:
for i in foo:bar:baz
do
    echo $i
done

Это приводит к следующему выводу:

foo bar baz

Разделение слов не выполняется на буквальном foo:bar:baz, поэтому не имеет значения, что такое значение IFS, что касается цикла for.

Разделение слов выполняется после расширения параметра на значении $i, поэтому foo:bar:baz разделяется на 3 слова, и передается на echo, поэтому выход foo bar baz.

Эксперимент 2

IFS=:
for i in foo:bar:baz
do
    unset IFS
    echo $i
done

Это приводит к следующему выводу:

foo:bar:baz

Еще раз, разделение слов не выполняется на буквальном foo:bar:baz, поэтому не имеет значения, что такое значение IFS, что касается цикла for.

Разделение слов выполняется после расширения параметра на значении $i, но поскольку IFS не был установлен, его значение по умолчанию используется для выполнения разделения, который равен <space><tab><newline>. Но поскольку foo:bar:baz не содержит ни одного из них, он остается неповрежденным, поэтому выход foo:bar:baz.

Эксперимент 3

IFS=:
var=foo:bar:baz
for i in $var
do
    echo $i
done

Это приводит к следующему выводу:

foo
bar
baz

После расширения параметра $var разбиение слов выполняется с использованием значения IFS, и поэтому for имеет 3 значения для итерации, foo, bar и baz. Поведение echo здесь тривиально, вывод - одно слово на строку.


Нижняя линия: разбиение слов не выполняется по буквальным значениям. Разделение слов выполняется только при выполнении определенных операций.

Это не удивительно. Строковый литерал очень похож на выражение, заключенное в двойные кавычки, и вы не ожидаете разделения слов на "...".