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

Вы могли всегда сохранять старую версию своей любимой ОС для этой задачи со старыми драйверами, работающими в VM (например, VirtualBox).

2
21.03.2012, 22:13
2 ответа

Часть проблемы - то, что строка, возвращенная args.sh, не анализируется то же как прямая команда, но только значением $IFS ($' \t\n'). Попытайтесь включить трассировку команды с set -x:

$ sh /tmp/test.sh $(sh /tmp/args.sh)
++ sh /tmp/args.sh
+ sh /tmp/test.sh 1 '"Two' 'words"' 3
Argument 1: 1
Argument 2: "Two
Argument 3: words"
$

Заметьте строку, запускающуюся с сингла +: существует четыре аргумента, '"Two' и 'words"' два проанализированных как отдельные аргументы. То, что Вы хотели бы сделать, изменить $IFS.

$ set -x
$ IFS='"'
+ IFS='"'
$ sh /tmp/test.sh $(sh /tmp/args.sh)
++ sh /tmp/args.sh
+ sh /tmp/test.sh '1 ' 'Two words' ' 3'
Argument 1: 1
Argument 2: Two words
Argument 3:  3
$

Это не будет работать на каждый вывод. Лучшая вещь сделать состоит в том, чтобы изменить вывод args.sh для разделения вывода чем-то другим от пространства, например, запятая или двоеточие:

$ cat /tmp/args.sh
#!/bin/sh
#This script is named: args.sh

echo "1,Two words,3"
$ IFS=,
$ sh /tmp/test.sh $(sh /tmp/args.sh)
+ sh /tmp/args.sh
+ sh /tmp/test.sh 1 Two words 3
Argument 1: 1
Argument 2: Two words
Argument 3: 3
$
4
27.01.2020, 22:01

Когда Вы оставляете подстановку переменных $var или замена команды $(cmd) неупомянутый, результат подвергается следующим преобразованиям:

  1. Разделите получившую строку на слова. Разделение происходит в пробеле (последовательности пространства, вкладок и новых строк); это может быть настроено путем установки IFS (см. 1, 2).
  2. Каждое получающееся слово рассматривают как шаблон шарика, и если это соответствует некоторым файлам, слово заменяется списком имен файлов.

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

Общее правило программирования оболочки состоит в том, чтобы всегда помещать двойные кавычки вокруг переменной и замен команды, если Вы не знаете, почему необходимо оставить их. Таким образом в test.sh, записать echo "Argument 1: $1". Передать аргументы test.sh, Вы в беде: необходимо передать список слов от args.sh кому: test.sh, но Ваш выбранный метод включает замену команды и который только обеспечивает путь к простой строке.

Если можно гарантировать, что аргументы, которые будут переданы, не содержат новых строк, и приемлемо изменить процесс вызова немного, можно установить IFS содержать только новую строку. Удостоверьтесь это args.sh выводы точно одно имя файла на строку без случайных кавычек.

IFS='
'
test.sh $(args.sh)
unset IFS

Если аргументы могут содержать произвольные символы (по-видимому, кроме пустых байтов, которые Вы не можете передать как аргументы), необходимо будет выполнить некоторое кодирование. Любое кодирование сделает; конечно, это не совпадет с передачей аргументов непосредственно: это не возможно. Например, в args.sh (замена \t фактическим символом табуляции, если Ваша оболочка не поддерживает его):

for x; do
  printf '%s_\n' "$x" |
  sed -e 's/q/qq/g' -e 's/ /qs/g' -e 's/\t/qt/g' -e 's/$/qn/'
done

и в test.sh:

for arg; do
  decoded=$(printf '%s\n' "$arg" |
            sed -e 's/qs/ /g' -e 's/qt/\t/g' -e 's/qn/\n/g' -e 's/qq/q/g')
  decoded=${decoded%_qn}
  # process "$decoded"
done

Можно предпочесть изменяться test.sh принять список строк, как введено. Если строки не содержат новые строки, вызвать args.sh | test.sh и используйте это в args.sh (объяснение):

while IFS= read -r line; do
  # process "$line"
done

Другой метод, который предотвращает потребность в заключении в кавычки в целом, должен вызвать второй сценарий сначала.

…
args.sh "$@"
0
27.01.2020, 22:01

Теги

Похожие вопросы