Что IFS = делает в этом цикле bash: `cat file | в то время как IFS = read -r line; сделать... сделано`

Я изучаю bash, и я увидел эту конструкцию:

cat file | while IFS= read -r line;
do
    ...
done

Может ли кто-нибудь объяснить, что делать IFS=? Я знаю это поле ввода sepereter, но оно ничего не задано?

Ответ 1

IFS делает много вещей, но вы спрашиваете об этом конкретном цикле.

Эффект в этом цикле заключается в сохранении верхнего и конечного пробелов в line. Чтобы проиллюстрировать это, сначала наблюдайте, что IFS ничего не задано:

$ echo " this   is a test " | while IFS= read -r line; do echo "=$line=" ; done
= this   is a test =

Переменная line содержит все пустое пространство, полученное на его stdin. Теперь рассмотрим тот же оператор с IFS по умолчанию:

$ echo " this   is a test " | while read -r line; do echo "=$line=" ; done
=this   is a test=

В этой версии белое пространство, внутреннее для линии, сохраняется. Но начальное и конечное пробелы были удалены.

Что делает -r в read -r?

Параметр -r не позволяет read рассматривать обратную косую черту как специальный символ.

Чтобы проиллюстрировать, мы используем две команды эха, которые снабжают две строки в цикле while. Обратите внимание, что происходит с -r:

$ { echo 'this \\ line is \' ; echo 'continued'; } | while IFS= read -r line; do echo "=$line=" ; done
=this \\ line is \=
=continued=

Теперь наблюдайте, что происходит без -r:

$ { echo 'this \\ line is \' ; echo 'continued'; } | while IFS= read line; do echo "=$line=" ; done
=this \ line is continued=

Без -r произошли два изменения. Во-первых, двойная обратная косая черта была преобразована в единую обратную косую черту. Во-вторых, обратная косая черта в конце первой строки интерпретировалась как символ продолжения линии, и две линии были объединены в один.

В целом, если вы хотите, чтобы обратные косые черты во входном значении имели особое значение, не используйте -r. Если вы хотите, чтобы обратные косые черты во входном файле принимались как простые символы, используйте -r.

Несколько строк ввода

Так как read принимает ввод по одной строке за раз, IFS ведет себя, влияет на каждую строку с несколькими линейными входами так же, как на однострочный вход. -r ведет себя аналогично исключению, что без -r несколько строк могут быть объединены в одну строку с использованием обратной обратной косой черты, как показано выше.

Поведение с несколькими линейными вводами можно, однако, резко изменить, используя флаг -d. -d изменяет символ разделителя, который read использует для обозначения конца строки ввода. Например, мы можем завершить строки с символом табуляции:

$ echo $'line one \n line\t two \n line three\t ends here'
line one 
 line    two 
 line three      ends here
$ echo $'line one \n line\t two \n line three\t ends here' | while IFS= read -r -d$'\t' line; do echo "=$line=" ; done
=line one 
 line=
= two 
 line three=

Здесь конструкция $'...' использовалась для ввода специальных символов, таких как newline, \n и tab, \t. Обратите внимание, что -d$'\t', read делит свой вход на "строки" на основе символов табуляции. Все, что после последней вкладки игнорируется.

Как обрабатывать наиболее сложные имена файлов

Самое важное использование описанных выше функций - обработка сложных имен файлов. Поскольку один символ, который не может отображаться в path/filenames, является нулевым символом, нулевой символ может использоваться для разделения списка имен файлов. В качестве примера:

while IFS= read -r -d $'\0' file
do
    # do something to each file
done < <(find ~/music -type f -print0)