Как переменная Bash IFS влияет на подстановку команд?

Я пишу сценарий bash, который зацикливается на выводе подстановки команды, а затем пытается выполнить другую подстановку команды в теле цикла. Вот код:

#!/usr/bin/bash

IFS=$'\n' 

for i in $( xmllint --xpath "string(/*[local-name()='Project'])" gsGDAL/gsGDAL.vcxproj.user )
do
   IFS=' ' #attempted with and without this line
   "$( awk -F= '{printf("export %s=\"%s\"", $1, $2)}' <(echo $i) )"
   IFS=$'\n' #attempted with and without this line
done

Я устанавливаю IFS на новую строку, чтобы цикл перебирал каждую СТРОКУ вывода команды xmllint, а не перебирал каждое слово, разделенное пробелами. Однако это приводит к сбою подстановки команд в цикле. Некоторая отладка привела к выводу, что суть проблемы такова:

#!/usr/bin/bash

IFS=$'\n'

$(echo export TEST="test")

что дает ошибку:

./x.sh: line 6: export TEST=test: command not found

Вы можете видеть, что я попытался исправить ошибку в первом примере кода, сбросив IFS в цикле. Это не сработало.

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

  1. Почему установка IFS на новую строку портит сгенерированный подстановкой команды экспорт?
  2. Почему сброс IFS в цикле не решает проблему?

ОБНОВЛЕНИЕ: Согласно обсуждению с Бармаром, IFS используется для разделения слов после расширения команды/переменной.


person user2141130    schedule 13.06.2013    source источник


Ответы (2)


Я не думаю, что IFS является причиной ваших проблем. В вашем коде две проблемы:

  1. Вы заключаете $(awk ...) в двойные кавычки, чтобы не было разделения слов. Таким образом, он обрабатывает export varname="value" как имя команды — пробел является частью имени команды, а не разделителем между командой и аргументом.

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

Решение этого, как указывает mplf, заключается в использовании eval:

eval "$(awk -F= '{printf("export %s=\"%s\"", $1, $2)}' <(echo $i) )"
person Barmar    schedule 13.06.2013
comment
Спасибо. Интересно, можете ли вы сказать мне, почему установка IFS на новую строку препятствует правильному выполнению результата подстановки команд. Это потому, что оболочка использует IFS для токенизации команды, в результате чего export TEST="test" интерпретируется как одиночный токен во время синтаксического анализа? И если да, то почему eval этому не подлежит (мой код работает без сброса IFS в пробел, когда я использую eval)? - person user2141130; 13.06.2013
comment
Да это правильно. При анализе командной строки оболочка использует разбиение на слова, чтобы найти команду — это просто первое слово. Если вы измените параметр для разделения слов, это повлияет на это. - person Barmar; 13.06.2013
comment
Это так, поэтому вам все равно нужно сделать IFS=' ' перед eval. - person Barmar; 13.06.2013
comment
Я обнаружил, что он работает без сброса на IFS=' '. И, чтобы быть уверенным, я отключил экспортированные значения перед запуском скрипта. - person user2141130; 13.06.2013
comment
Моя ошибка. IFS используется только для разделения слов после раскрытия команды/переменной, а не для разбора самой команды. - person Barmar; 13.06.2013

Используйте eval для оценки строки, возвращаемой подоболочкой.

IFS=' ' #attempted with and without this line
eval $( awk -F= '{printf("export %s=\"%s\"", $1, $2)}' <(echo $i) )
IFS=$'\n' #attempted with and without this line
person mplf    schedule 13.06.2013