Каково точное значение IFS=$'\n'?

Если следующий пример, который устанавливает переменную среды IFS в символ перевода строки...

IFS=$'\n'
  • Что означает знак доллара точно?
  • Что он делает в данном конкретном случае?
  • Где я могу узнать больше об этом конкретном использовании (Google не разрешает использовать специальные символы в поиске, и я не знаю, что искать в противном случае)?

Я знаю, что такое переменная среды IFS и что такое символ \n (перевод строки), но почему бы просто не использовать следующую форму: IFS="\n" (которая не работает)?

Например, если я хочу перебрать каждую строку файла и использовать цикл for, я мог бы сделать это:

for line in (< /path/to/file); do
    echo "Line: $line"
done

Однако это не будет работать правильно, если IFS не установлен на символ перевода строки. Чтобы заставить его работать, я должен был бы сделать это:

OLDIFS=$IFS
IFS=$'\n'
for line in (< /path/to/file); do
    echo "Line: $line"
done
IFS=$OLDIFS

Примечание: мне не нужен другой способ сделать то же самое, я уже знаю много других... Мне просто интересно узнать об этом $'\n', и мне интересно, может ли кто-нибудь дать мне объяснение по этому поводу.


person Yanick Girouard    schedule 08.11.2010    source источник


Ответы (7)


Обычно bash не интерпретирует управляющие последовательности в строковых литералах. Так что если вы пишете \n или "\n" или '\n', это не разрыв строки — это буква n (в первом случае) или обратная косая черта, за которой следует буква n (в двух других случаях).

$'somestring' — это синтаксис строковых литералов с управляющими последовательностями. Таким образом, в отличие от '\n', $'\n' на самом деле является разрывом строки.

person sepp2k    schedule 08.11.2010
comment
Не совсем так — \n — это просто (экранированная) буква n. Вы правы, что '\n' и "\n" - это люфт, за которым следует n. - person Roman Cheplyaka; 09.11.2010
comment
Обратите внимание, что $'\n' специфичен для bash — он не будет работать в оболочке POSIX (/bin/sh). Чтобы получить тот же эффект в соответствии с POSIX, вы можете ввести IFS=', затем нажать клавишу возврата, чтобы ввести фактический символ новой строки, а затем ввести закрывающий ' - person Richard Hansen; 21.06.2011
comment
IFS=$(echo -e '\n') также должен делать это POSIX-совместимым способом. - person Vineet; 06.10.2011
comment
@Vineet - это дало мне паузу, чтобы оспорить комментарий, за который проголосовали. Хотя это корректно для Posix, оно не работает — операторы подстановки команд в bash удаляют все конечные символы новой строки. См. это для более подробной информации. - person Digital Trauma; 05.10.2013
comment
Убедитесь, что скрипт начинается с !/bin/bash, а не !/bin/sh. Не актуально, но может быть ловушкой. - person nvd; 04.09.2014
comment
@DigitalTrauma Я думаю, что это даже не POSIX: -e не определено, а \n без -e работает как расширение XSI: pubs.opengroup.org/onlinepubs/9699919799/utilities/ . printf '\n' рулит ;) - person Ciro Santilli 新疆再教育营六四事件ۍ 10.10.2014
comment
Довольно неожиданный побочный эффект использования IFS='\n' (без знака доллара) заключается в том, что ваше имя пользователя усекается, если в нем есть n (из-за этой ошибки я попал в эту тему). echo $USER дает brade вместо braden в моем случае. - person Braden Best; 27.12.2016
comment
@Richard Hansen: это не работает в моем bash. Изменилось ли это в более новых версиях bash? Я использую версию 4.2.46(2) - person anishjp; 28.11.2019
comment
Вот реальное решение POSIX: nl="$(printf '\nx')"; nl="${nl%x}"; IFS="$nl". Печатая дополнительный символ с нашей новой строкой, новая строка не теряется при подстановке команд. Затем мы просто используем расширение параметра, чтобы удалить лишний символ. - person Andrey Kaipov; 28.05.2020

Просто чтобы дать конструкции ее официальное название: строки в форме $'...' называются Строки в ANSI-кавычках.

То есть, как и в строках [ANSI] C, экранирующие последовательности распознаются и расширяются до их буквального эквивалента (см. ниже полный список поддерживаемых управляющих последовательностей).

После этого расширения $'...' строки ведут себя так же, как '...' строки, т. е. они рассматриваются как литералы, НЕ подлежащие никаким [дальнейшие] расширения оболочки.

Например, $'\n' заменяется на символ новой строки, чего не может сделать обычный строковый литерал bash (будь то '...' или "...").[1]

Еще одна интересная особенность заключается в том, что строки в ANSI C-кавычках могут экранировать ' (одинарные кавычки) как \', чего '...' (обычные строки в одинарных кавычках) не могут:

echo $'Honey, I\'m home' # OK; this cannot be done with '...'

Список поддерживаемых escape-последовательностей:

Управляющие последовательности обратной косой черты, если они присутствуют, декодируются следующим образом:

\ оповещение (звонок)

\b назад

\e \E escape-символ (не ANSI C)

\f подача формы

\n новая строка

\r возврат каретки

\t горизонтальная вкладка

\v вертикальная вкладка

\ обратная косая черта

\' одинарная кавычка

\" двойная кавычка

\nnn восьмибитный символ, значение которого представляет собой восьмеричное значение nnn (от одной до трех цифр)

\xHH восьмибитный символ, значение которого является шестнадцатеричным значением HH (одна или две шестнадцатеричные цифры)

\uHHHH символ Unicode (ISO/IEC 10646), значение которого представляет собой шестнадцатеричное значение HHHH (от одной до четырех шестнадцатеричных цифр)

\UHHHHHHHH символ Unicode (ISO/IEC 10646), значение которого представляет собой шестнадцатеричное значение HHHHHHHH (от одной до восьми шестнадцатеричных цифр)

\cx символ управления-x

Расширенный результат заключен в одинарные кавычки, как если бы знака доллара не было.


[1] Однако вы можете вставлять фактические новые строки в строки '...' и "..."; т. е. вы можете определить строки, которые охватывают несколько строк.

person mklement0    schedule 13.03.2015

Из http://www.linuxtopia.org/online_books/bash_guide_for_beginners/sect_03_03.html:

Слова в форме "$'STRING'" обрабатываются особым образом. Слово заменяется на строку, в которой символы, экранированные обратной косой чертой, заменяются в соответствии со стандартом ANSI-C. Эскейп-последовательности обратной косой черты можно найти в документации Bash.found

Я предполагаю, что это заставляет скрипт избегать перевода строки на правильный стандарт ANSI-C.

person Brad Swerdfeger    schedule 08.11.2010

Повторное восстановление IFS по умолчанию — это OLDIFS=$IFS не требуется. Запустите новый IFS в подоболочке, чтобы избежать переопределения IFS по умолчанию:

ar=(123 321); ( IFS=$'\n'; echo ${ar[*]} )

Кроме того, я не очень верю, что вы полностью восстановите старый IFS. Вы должны использовать двойные кавычки, чтобы избежать разрыва строки, например OLDIFS="$IFS".

person Marek    schedule 08.04.2013
comment
это действительно полезная техника. я просто использовал его для более чистой операции присоединения к оболочке: args=$(IFS='&'; echo "$*"). восстановление IFS в $' \t\n' в дружественной оболочке Bourne манере - немалый подвиг. - person jeberle; 11.03.2014
comment
Относительно Besides I don't really believe you recover the old IFS fully: разбиение слов не выполняется в правой части присваивания переменных (но выполняется удаление кавычек), поэтому OLDIFS=$IFS и OLDIFS="$IFS" ведут себя одинаково. - person mklement0; 13.03.2015

Строки ANSI в C-кавычках являются ключевым моментом. Спасибо @mklement0.

Вы можете протестировать строки ANSI в C-кавычках с помощью команды od.

echo -n $'\n' | od -c
echo -n '\n' | od -c
echo -n $"\n" | od -c
echo -n "\n" | od -c

Выходы:

0000000  \n  
0000001

0000000   \   n   
0000002

0000000   \   n   
0000002

0000000   \   n   
0000002

Вы можете четко знать значение выходов.

person Big Shield    schedule 11.01.2017

Вопрос:

Каково точное значение IFS=$'\n'?

Простой ответ:

Эй, Баш! установите Внутренний разделитель полей (IFS) на Новая строка


Что такое IFS?

IFS — это символ, который Bash использует в качестве границ слова/элемента при обработке строк символов.

Он настроен на пробельные символы пробел, табуляция и новая строка, по умолчанию.

Пример 1:

Использовать значение по умолчанию для IFS

string="first second:third forth:fifth"

for item in $string; do
    echo "$item"
done

Вывод:

first
second:third
forth:fifth

Пример 2:

Установите IFS на :

# Set the IFS to collon (:) character
IFS=:

string="first second:third forth:fifth"

for item in $string; do
    echo "$item"
done

Вывод:

first second  
third forth  
fifth
person Pmpr    schedule 04.04.2021

Это похоже на получение значения из переменной:

VAR='test'
echo VAR
echo $VAR

отличаются, поэтому знак доллара в основном оценивает содержание.

person Pieter    schedule 08.11.2010
comment
Это не имеет ничего общего с переменными. $'FOO' (в отличие от $FOO, о котором этот вопрос не был) является строковым литералом. Если вы выполните echo $'VAR', вы увидите, что он печатает строку VAR, а не test. - person sepp2k; 09.11.2010