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

163

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

IFS=$'\n'

В чем смысл этой коллекции символов?

Абдул Аль Хазред
источник
3
См. Также unix.stackexchange.com/questions/26784/understanding-ifs и приведенные в нем вопросы.
Жиль
синтаксис vim подчеркивает это как ошибку для меня; исправить?
theonlygusti
IFS=$'\n'является bashism (+ другие оболочки, использовать ANSI-C Цитирование , для обхода см stackoverflow.com/questions/10748703/...
pevik

Ответы:

199

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

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

mystring="foo:bar baz rab"
for word in $mystring; do
  echo "Word: $word"
done

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

Word: foo:bar
Word: baz
Word: rab

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

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

Word: foo
Word: 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(с вариациями между всеми оболочками). Цитирую man-страницу bash:

Слова вида $ 'string' обрабатываются специально. Слово расширяется до «строки», символы с обратной косой чертой заменяются в соответствии со стандартом ANSI C.

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

Tblue
источник
3
Это хорошо, но, на мой взгляд, вам лучше читать и понимать спецификацию POSIX, а не bashруководство по написанию сценариев или что-то еще. В основном, информация, доступная по таким ссылкам, не хватает важными способами. Во всяком случае, таким образом, упускается два важных момента, касающихся расщепления оболочки - глобализация и пропуски IFS.
mikeserv
@mikeserv Спасибо, я добавил информацию о пробелах IFS. Не знал об этом. :)
Tblue
4
Не так актуально, но если вам интересно, вы можете посмотреть, как unset IFSоболочка ведет себя совсем иначе, чем IFS=. Кроме того, первый байт в IFS также особенный, "${named_array[*]}"но не имеет значения, когда расширение не
заключено в кавычки
Еще несколько моментов: 1-разбиение слов, управляемое с помощью $IFS- одна из двух основных вещей, выполняемых при раскрытии переменной без кавычек в контексте списка (это splitчасть split+globоператора). Другой болтается. При использовании разделения работы, как правило, необходимо set -fотключить эту globчасть.
Стефан Шазелас
3
3- $IFSтакже используется readвстроенной командой
Стефан Шазелас
22

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

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

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

choroba
источник
5
+1 за упоминание, dollared single quotesкоторое отличается от простых одинарных кавычек.
Snowcrash
2
@Snowcrash +1 за высказывание +1 за упоминание о одиночных кавычках, которые отличаются от простых одинарных кавычек . Извините, ничего не могу поделать :) Но на самом деле это очень хорошая вещь, которую нужно отметить, потому что это важно!
Прифтан
1
@ Прифтан +1 за +1 за +1 ... понимаешь ... это действительно важно.
0xc0de
@ 0xc0de Определенно согласен! Спасибо за это! :)
Прифтан
15

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

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

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

cuonglm
источник
-1

Я предпочел объяснить $IFSна примере:
если вы хотите, чтобы cp, mv или другой файл обрабатывались, IFS пуст по умолчанию, когда ваши файлы имеют метасимвол или пробел, например:
Linux Administration.pdfили Free Software Fundation.ogg, конечно, у вас будет проблема, потому что: Linux рассматривает отдельный параметр и Администрация рассматривают отдельный параметр. Итак, bash имеет built-in variable, затем вы можете инициализировать 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
Персидский залив
источник