shell - temp IFS только как новая строка. Почему это не работает: IFS=$(echo -e '\n')

Я пытаюсь использовать for в оболочке для перебора имен файлов с пробелами. Я прочитал в вопрос stackoverflow что IFS=$'\n' можно использовать, и это работает нормально. Затем я прочитал в комментарии к одному из ответов, чтобы сделать его совместимым с posix для оболочек, отличных от bash, можно использовать IFS=$(echo -e '\n').

Первый работает, а второй нет. Комментарий проголосовали несколько раз. Я проверил вывод, и переменная, похоже, не содержит новой строки.

#!/bin/sh

IFS=$(echo -e '\n')
echo -n "$IFS" | od -t x1
for file in `printf 'one\ntwo two\nthree'`; do
    echo "Found $file"
done

echo

IFS=$'\n'
echo -n "$IFS" | od -t x1
for file in `printf 'one\ntwo two\nthree'`; do
    echo "Found $file"
done


Вывод показывает, что разделитель полей отсутствует для первого:

0000000
Found one
two two
three


Но правильно на втором:

0000000 0a
0000001
Found one
Found two two
Found three


Я нашел ответ на вопрос, который может быть или не быть дубликатом моего вопроса:
linux — При настройке IFS для разделения на новые строки, почему необходимо включать backspace? - Переполнение стека

Опасно ли делать то, что там обсуждается, IFS=$(echo -en '\n\b')? Потому что это добавляет \b к IFS (я проверял). Может ли \b встречаться в имени файла? Я не хочу, чтобы мой код по ошибке разбивал имя файла.

Также есть ли у вас какие-либо рекомендации о том, как правильно обрабатывать восстановление IFS после цикла for? Должен ли я хранить его во временной переменной или запускать IFS и оператор for в подоболочке? (и как это сделать?) спасибо


person user2672807    schedule 04.10.2013    source источник


Ответы (3)


Обновить — заменить мой псевдокомментарий реальным ответом.

Я думаю, что этот ответ должны объяснить поведение, которое вы видите. В частности, операторы подстановки команд $() и обратные кавычки будут удалять завершающие символы новой строки из вывода команды. Однако прямое назначение в вашем втором примере не выполняет подстановку команд, поэтому работает так, как ожидалось.

Поэтому я боюсь сказать, что считаю комментарий, на который вы ссылаетесь, неверным.


Я думаю, что самый безопасный способ восстановить IFS — установить его в подоболочке. Все, что вам нужно сделать, это поместить соответствующие команды в круглые скобки ():

(
    IFS=$'\n'
    echo -n "$IFS" | od -t x1
    for file in `printf 'one\ntwo two\nthree'`; do
        echo "Found $file"
    done
)

Конечно, вызов подоболочки влечет за собой небольшую задержку, поэтому необходимо учитывать производительность, если это должно повторяться много раз.


Кроме того, будьте очень осторожны, имена файлов могут содержать как \b, так и \n. Я думаю, что единственные символы, которые они не могут содержать, это косая черта и \0. По крайней мере, так написано в этой статье в Википедии.

$ touch $'12345\b67890'
$ touch "new
> line"
$ ls --show-control-chars
123467890  new
line
$ 
person Digital Trauma    schedule 05.10.2013

Поскольку новые строки удаляются путем подстановки команд, как правильно написал @DigitalTrauma, вы должны использовать другой, совместимый с POSIX способ.

IFS='
'

пишет новую строку и присваивает ей. Просто и эффективно.

Если вам не нравятся задания, занимающие две строки, вы можете использовать printf в качестве альтернативы.

IFS="$(printf '\n')"
person Riccardo Galli    schedule 02.10.2014
comment
Спасибо за информацию буду иметь в виду - person user2672807; 10.10.2014
comment
Удалите второй пример, так как он нечеткий: IFS="$(printf '\n+')" $ echo -n "$IFS" | xxd 00000000: 0a2b ‹- Это также добавляет + к IFS. - person Tom Hale; 05.10.2016
comment
@TomHale Я удалил ненужный + - person Riccardo Galli; 06.10.2016
comment
Вы проверили это? Это не работает по причине, указанной здесь. - person Tom Hale; 06.10.2016
comment
Я сделал, это работает. Кстати, я тестирую rxvt-unicode на Ubuntu (так что тире, я полагаю, для /bin/sh) - person Riccardo Galli; 06.10.2016

IFS=$(printf '\n+'); IFS=${IFS%?}

Как упоминалось другими, подстановка команды удаляет завершающие символы новой строки. Итак, если вы хотите что-то читаемое, добавьте символ после новой строки (в данном случае «+»), чтобы предотвратить его удаление, а затем используйте расширение параметра, чтобы удалить символ-заполнитель.

person Compholio    schedule 09.04.2021