Вопрос или проблема
В начале сценария оболочки 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
).
- С
IFS=:
строка будет разделена на четыре поля:"a"
,"b"
,""
(пустая строка) и" c d "
(снова два пробела междуc
иd
). Обратите внимание на ведущие и замыкающие пробелы в последнем поле. - С
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 будет использовать только символ новой строки в качестве разделителя. Таким образом, все остальные символы (пробелы, табуляция или другие символы) будут игнорироваться, и каждая строка будет рассматриваться как отдельное поле.
Примеры использования:
- Использование IFS со значениями по умолчанию:
При выполнении следующего кода:
mystring="foo:bar baz rab"
for word in $mystring; do
echo "Слово: $word"
done
При использовании значения IFS
, которое есть по умолчанию (пробел, табуляция, новая строка), результат будет:
Слово: foo:bar
Слово: baz
Слово: rab
Здесь bash
считает пробел и двоеточие в качестве границ между словами.
- Изменение IFS на двоеточие:
Если вы установите IFS=:
перед выполнением цикла, то результат будет:
IFS=:
mystring="foo:bar baz rab"
for word in $mystring; do
echo "Слово: $word"
done
Результат:
Слово: foo
Слово: bar baz rab
Теперь bash разделяет строку только по двоеточию, игнорируя пробелы.
- Применение
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 скриптах используется для изменения внутреннего разделителя полей на символ новой строки, что позволяет более точно обрабатывать строки, не обращая внимания на другие символы, такие как пробелы и табуляции. Это делает обработку данных более надежной и предсказуемой.