Расширение в кавычках и без кавычек

11
  1. for i in $(xrandr); do echo "$i" ; done
  2. for i in "$(xrandr)"; do echo "$i"; done
  3. for i in "$(xrandr)"; do echo $i; done

Я понимаю, почему 1 отличается от 2. Но почему 3 дает отличный результат от 2? Пожалуйста, объясните вывод тоже. Как цитаты работают на новых строках?

ManuelSchneid3r
источник

Ответы:

19

Переменная без кавычек (как в $var) или подстановка команд (как в $(cmd)или `cmd`) - это оператор split + glob в оболочках типа Борна.

То есть их содержимое разделяется в соответствии с текущим значением $IFSспециальной переменной (которая по умолчанию содержит пробел, символы табуляции и символы новой строки)

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

Таким for i in $(xrandr)образом $(xrandr), поскольку он не находится в кавычках, он разбит на последовательности символов пробела, табуляции и новой строки. И каждое слово, полученное в результате этого разбиения, проверяется на совпадение имен файлов (или оставляется как есть, если они не совпадают ни с одним файлом) и forпроходит по всем.

В for i in "$(xrandr)", мы не используем оператор split + glob, так как подстановка команды заключена в кавычки, поэтому в цикле есть одно проходное значение для одного значения: выход xrandr(без завершающих символов новой строки, которые заменяет подстановка команды ).

Однако в echo $i, $iснова не заключаются в кавычки, поэтому снова содержимое $iразделяется и подвергается генерации имени файла, и они передаются echoкоманде как отдельные аргументы (и echoвыводят ее аргументы через пробелы).

Итак, урок усвоен:

  • если вы не хотите разбивать слова или генерировать имена файлов , всегда заключайте в кавычки расширения переменных и подстановки команд
  • если вы хотите разделить слова или создать имя файла , оставьте их без кавычек, но установите их $IFSсоответствующим образом и / или включите или отключите создание имени файла, если необходимо ( set -f, set +f).

Как правило, в приведенном выше примере, если вы хотите перебрать пустой разделенный список слов в выходных данных xrandr, вам необходимо:

  • оставить значение $IFSпо умолчанию (или сбросить его) для разделения на пробелы
  • Используйте, set -fчтобы отключить генерацию имени файла, если вы не уверены, что xrandrникогда не выводите символы *или ?или [(которые являются подстановочными знаками, используемыми в шаблонах генерации имени файла)

И только затем используйте оператор split + glob (оставьте подстановку команды или расширение переменной без кавычек) в inчасти forцикла:

set -f; unset -v IFS
for i in $(xrandr); do whatever with "$i"; done

Если вы хотите , чтобы цикл по (непустым) линиям в xrandrвыходе, вы должны установить $IFSна символ новой строки:

IFS='
'
Стефан Шазелас
источник
4

Цитируемый перевод строки - это перевод строки. Таким образом, echo "$1"дает один аргумент командной строки для echo, который затем печатает новые строки напрямую.

Новая строка без кавычек - это пробел. Таким образом, echo $1дает много аргументов командной строки для эха, который печатает их один за другим через пробел.

RICi
источник
Новая строка без кавычек - это пробел , слишком короткий путь, чтобы быть действительно полезным в качестве объяснения поведения здесь IMO.
Стефан Шазелас