Что означает IFS=$’\n’ в сценариях bash?

Вопрос или проблема

В начале сценария оболочки bash находится следующая строка:

IFS=$'\n'

Каково значение этой комбинации символов?

IFS означает “внутренний разделитель полей”. Он используется оболочкой для определения того, как выполнять разбиение слов, т. е. как распознавать границы слов.

Попробуйте это в оболочке, такой как bash (другие оболочки могут обрабатывать это по-другому, например, zsh):

mystring="foo:bar baz rab"
for word in $mystring; do
  echo "Слово: $word"
done

Значение по умолчанию для IFS состоит из пробельных символов (точнее: пробел, табуляция и новая строка). Каждый символ может быть границей слова. Таким образом, с значением по умолчанию IFS цикл выше выведет:

Слово: foo:bar
Слово: baz
Слово: rab

Другими словами, оболочка считает, что пробел является границей слова.

Теперь попробуйте установить IFS=: перед выполнением цикла. На этот раз результат будет таким:

Слово: foo
Слово: bar baz rab

Теперь оболочка делит mystring на слова тоже — но на этот раз только двоеточие рассматривается как граница слова.

Первый символ IFS является специальным: он используется для разделения слов в выводе при использовании специальной переменной $* (пример взят из Руководства по расширенному скрипту bash, где вы также можете найти больше информации о специальных переменных, таких как эта):

$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
w:x:y:z

Сравните с:

$ bash -c 'set w x y z; IFS="-:;"; echo "$*"'
w-x-y-z

Обратите внимание, что в обоих примерах оболочка по-прежнему рассматривает все символы :, - и ; как границы слов. Единственное, что меняется, это поведение $*.

Еще одна важная вещь, которую нужно знать, это то, как обрабатываются так называемые “пробелы IFS” обрабатываются. В основном, как только IFS включает пробельные символы, ведущие и замыкающие пробелы удаляются из строки, подлежащей разбиению, перед ее обработкой, и последовательность нескольких последовательных пробельных символов также разделяет поля. Однако это применяется только к тем пробельным символам, которые фактически присутствуют в IFS.

Например, рассмотрим строку "a:b:: c d " (с замыкающим пробелом и двумя пробелами между c и d).

  1. С IFS=: строка будет разделена на четыре поля: "a", "b", "" (пустая строка) и " c d " (снова два пробела между c и d). Обратите внимание на ведущие и замыкающие пробелы в последнем поле.
  2. С IFS=' :' строка будет разделена на пять полей: "a", "b", "" (пустая строка), "c" и "d". Никаких ведущих и замыкающих пробелов.

Обратите внимание, что несколько последовательных пробельных символов разделяют два поля во втором примере, в то время как несколько последовательных двоеточий этого не делают (поскольку это не пробельные символы).

Что касается IFS=$'\n', это синтаксис ksh93, который также поддерживается bash, zsh, mksh и FreeBSD sh (с различиями между всеми оболочками). Цитируя справочную страницу bash:

Слова вида $’строка’ обрабатываются особым образом. Слово развертывается в “строку”, а символы, экранированные знаком обратной косой черты, заменяются в соответствии со стандартом ANSI C.

\n — это управляющая последовательность для новой строки, поэтому IFS в конечном итоге устанавливается в один символ новой строки.

Внутри долларовых одинарных кавычек некоторые символы обрабатываются особым образом. Например, \n преобразуется в новую строку.

Таким образом, эта конкретная строка присваивает новую строку переменной IFS. IFS, в свою очередь, является специальной переменной в bash: Внутренний разделитель полей. Как говорит man bash, он

используется для разбиения слов после развертывания и для разбиения строк на слова с помощью встроенной команды read. Значение по умолчанию — <пробел><табуляция><новая строка>.

Вкратце, IFS=$'\n' присваивает новую строку \n переменной IFS.

Конструкция $'строка' является механизмом цитирования, который используется для декодирования последовательностей экранирования, подобных ANSI C. Этот синтаксис пришел из ksh93 и был портирован в современные оболочки, такие как bash, zsh, pdksh, busybox sh.

Этот синтаксис не определен POSIX, но был принят для вопроса SUS 7.

Я предпочел объяснить $IFS на примере:
Предположим, вы хотите скопировать или переместить или выполнить другую обработку файлов, IFS по умолчанию пуст. Когда ваши файлы содержат метаперсонажи или пробелы, такие как:
Linux Administration.pdf или Free Software Fundation.ogg, конечно, у вас возникнут проблемы. Потому что:
Linux рассматривает это как отдельный параметр, а Administration рассматривает это как отдельный параметр. Таким образом, bash имеет встроенную переменную, которая позволяет вам инициализировать IFS=$(echo -en "\n\b"), тогда bash игнорирует любые метаперсонажи и пробелы между именами файлов. Например:

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
mymusicdir=~/test/dd
find $mymusicdir -name "*" -execdir rename 's/ /_/g' "{}" +
IFS=$SAVEIFS

Внутренний разделитель полей (IFS)

Упс –>

for F in $(ls -1) ; do echo "${F}" ; done 

01 
file.txt
02
file.txt
03
file.txt

используйте так:

bash>IFS=$'\n' ; for F in $(ls -1) ; do echo "${F}" ; done 

01 file.txt
02 file.txt
03 file.txt

Ответ или решение

Вопрос: Каково значение выражения IFS=$'\n' в скриптах bash?

Ответ:

IFS (Internal Field Separator) является специальной переменной в bash, которая определяет символы, используемые для разделения полей при обработке строк. По умолчанию IFS содержит пробелы, табуляцию и символ новой строки (newline).

Запись IFS=$'\n' устанавливает IFS равным символу новой строки. Это значит, что при разделении строки на слова в процессе выполнения команд, bash будет использовать только символ новой строки в качестве разделителя. Таким образом, все остальные символы (пробелы, табуляция или другие символы) будут игнорироваться, и каждая строка будет рассматриваться как отдельное поле.

Примеры использования:

  1. Использование IFS со значениями по умолчанию:

При выполнении следующего кода:

mystring="foo:bar baz rab"
for word in $mystring; do
  echo "Слово: $word"
done

При использовании значения IFS, которое есть по умолчанию (пробел, табуляция, новая строка), результат будет:

Слово: foo:bar
Слово: baz
Слово: rab

Здесь bash считает пробел и двоеточие в качестве границ между словами.

  1. Изменение IFS на двоеточие:

Если вы установите IFS=: перед выполнением цикла, то результат будет:

IFS=:
mystring="foo:bar baz rab"
for word in $mystring; do
  echo "Слово: $word"
done

Результат:

Слово: foo
Слово: bar baz rab

Теперь bash разделяет строку только по двоеточию, игнорируя пробелы.

  1. Применение IFS=$'\n':

Когда вы устанавливаете IFS равным символу новой строки:

IFS=$'\n'
mystring="foo:bar baz rab\nanother_line"
for word in $mystring; do
  echo "Слово: $word"
done

Результат будет:

Слово: foo:bar baz rab
Слово: another_line

Таким образом, строка была разделена только по новой строке, и в результате получились два "слова".

Причины использования:

Изменение значения IFS на \n может быть полезным в тех случаях, когда вам нужно правильно обработать строки, содержащие пробелы или другие специальные символы, которые могут вызвать проблемы при разбиении. Например, это может быть особенно актуально, когда вы работаете с именами файлов, содержащими пробелы или метасимволы.

Заключение:

IFS=$'\n' в bash скриптах используется для изменения внутреннего разделителя полей на символ новой строки, что позволяет более точно обрабатывать строки, не обращая внимания на другие символы, такие как пробелы и табуляции. Это делает обработку данных более надежной и предсказуемой.

Оцените материал
Добавить комментарий

Капча загружается...