Что означает 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конечном итоге устанавливается один символ новой строки.


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>.


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 .


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