Я читаю пару книг по скриптингу bash и пытаюсь понять правильное кавычки и использование IFS
. Возможно, кто-нибудь поможет мне с небольшим примером, включающим имена файлов с кавычками. Если сделать это из командной строки, то это работает для правильной распечатки имен файлов, даже если они включают пробелы:
set - *
for i in "$@"; do echo $i; done
Это не работает, так как разрывается на пробелах:
set - `find . -name "*"`
for i in "$@"; do echo $i; done
А также:
IFS=$'\0' set - `find . -name "*" -print0`
for i in "$@"; do echo $i; done
Также не работает комбинация, которая использует IFS=$'\n'
и -print
. Почему все они терпят неудачу?
Следующий вариант также не работает, но в этом случае возникает ошибка («bash: синтаксическая ошибка рядом с неожиданным токеном `do'»). Почему?
IFS=$'\n' for i in `find . -name "*" -type f`; do echo $i; done
но это работает (обратите внимание на ";"):
IFS=$'\n'; for i in `find . -name "*" -type f`; do echo $i; done
и это не удается, потому что имена файлов вообще не разделены ( for
циклы выполняются только один раз):
IFS=''; for i in `find . -name "*" -type f -print0`; do echo -e "$i\n"; done
Итак, еще раз, почему первый и третий пункты не срабатывают?
Наконец, прав ли я, полагая, что при установке IFS
, ''
то же самое, что и $'\0'
? (Я пробовал оба в предыдущем примере.) Если так, то почему мне, по-видимому, нужно , $'\n'
а не просто \n
?
*Bash — это версия 4.3.42(1) в Ubuntu Gnome 16.04.
решение1
Две вещи, которые я усвоил при отладке команд bash: во-первых, если что-то работает не так, как вы думаете, должно работать, выведите команды echo/printf, чтобы убедиться, что они отображаются так, как вы думаете. во-вторых, вручную запустите команды, которые есть в ваших командах, чтобы проверить, правильно ли они работают (если только это не потенциально разрушительные команды, такие как: rm, dd, chmod 777 и т. д.).
Как или чего именно вы пытаетесь добиться? У меня, кажется, все отлично работает с этой командой дословно, поэтому вам нужно будет объяснить, что вы хотите, чтобы она сделала. Есть ли у вас пробелы в именах файлов, которые вызывают проблемы с вашей командой? Если вы избавитесь от пробелов в переменной IFS, это, вероятно, сработает так, как вам нужно: IFS=$'\n\t'
. Эта -print0
опция в основном удаляет все разделители строк, поэтому все, что вы хотите получить от этих команд, не имеет разделителей вообще, если только в именах файлов нет пробелов. Для вашего второго набора вопросов:
IFS=$'\n' for i in `find . -name "*" -type f`; do echo $i; done
Эта команда не выполняется должным образом, поскольку bash считывает ее следующим образом:
IFS=$'\n' for i in `find . -name "*" -type f`
do echo $i
done
Первая строка устанавливает IFS в: $'\n' for i in
. После этого следующая строка читается как: do echo $i
, что не работает, так как do
команда требует, чтобы цикл какого-то рода был предшествующей командой. Следующая работает, так как точка с запятой сообщает bash, что достигнут конец команды, поэтому:
IFS=$'\n'
for i in `find . -name "*" -type f`
do echo $i
done
Наконец, прав ли я, полагая, что при установке IFS '' — это то же самое, что и $'\0'? (Я пробовал оба варианта в предыдущем примере.) Если так, то почему мне, по-видимому, нужно $'\n', а не просто \n?
Да, для первой части, ''
и '\0'
оба считаются NULL, так что они в основном одинаковы. Это связано с тем, что bash интерпретирует $''
как строку ANSI C. И вам нужно заключить \n в кавычки, чтобы bash интерпретировал его как строку C, чтобы символ новой строки был помещен в IFS.
Вы можете прочитать больше об этомздесь.