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

Предположим, у меня есть следующие два сценария оболочки:

#!/bin/sh #This script is named: args.sh echo 1 "\"Two words\"" 3 

, а также:

 #!/bin/sh #This script is named: test.sh echo "Argument 1: "$1 echo "Argument 2: "$2 echo "Argument 3: "$3 

Когда я вызываю сценарии как:

 sh test.sh $(sh args.sh) 

, Я получаю:

 Argument 1: 1 Argument 2: "Two Argument 3: words" 

Когда я ожидал, что вместо этого получим:

 Argument 1: 1 Argument 2: Two words Argument 3: 3 

Копирование вывода sh args.sh и вставка его в качестве входа sh test.sh работает нормально; поэтому я предполагаю, что это не то, что делает оболочка. Я могу достичь желаемого / ожидаемого результата, вызвав sh args.sh | xargs sh test.sh sh args.sh | xargs sh test.sh вместо этого.

Тем не менее, мне интересно, есть ли эквивалентный способ сделать это, не передавая вывод первого скрипта (args.sh) в xargs? Я хочу, чтобы сценарии вызывались в исходном порядке; сценарий аргумента для вывода параметров во второй. Я также ищу объяснение, почему этот вызов работает не так, как ожидалось?

2 Solutions collect form web for “Вывод сценария оболочки неправильно разбивается при передаче в качестве аргумента скрипту”

Часть проблемы состоит в том, что строка, возвращаемая 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 $ 

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

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

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

Общее правило программирования оболочки – всегда включать двойные кавычки вокруг переменных и подстановок команд, если вы не знаете, почему их нужно оставить. Итак, в 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 | test.sh и использовать это в args.sh ( объяснение ):

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

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

 … args.sh "$@" 
  • Как получить значение именованного параметра уже запущенного процесса в Linux?
  • Не удалось отслеживать ошибку
  • Попросите cron проверить, выполняется ли скрипт bash
  • При использовании $ hash_array
  • Параметры команды загрузки из файла верхнего уровня
  • Как вызвать функцию оболочки
  • как использовать shell-скрипт с snmpwalk с OID, чтобы иметь идентификатор vlan, имя vlan, порт принадлежит vlans в таблице Excel
  • Как показать результаты, найденные в выражении if then?
  • Bash, если утверждение не работает должным образом
  • Передача аргументов с пробелами и кавычками в скрипт (без цитирования всего)
  • Выход Capture (sdout / stderr) отправителя SSH-порта
  • Interesting Posts

    Debian сжимают (стабильные) сломанные пакеты после установки чего-то из хриплого (тестирования)

    ACL назвал групповые разрешения не переопределением прав доступа к файлам. Зачем?

    Разрешить Linux подключаться к адресам, содержащим домены с тире, за которыми следует точка

    Отображать только последние 10 (или n) строк длинного вывода команды

    Получение tmux для копирования буфера в буфер обмена

    notify-send не работает под управлением ssh

    в чем разница между vmlinuz vs zImage?

    Восстановление инкрементного резервного копирования Tar с помощью одной команды

    `.fritz.box` и` .local` имена хостов в одной сети: что мне действительно нужно?

    Как очистить и понизить пакеты PPA с прекращенной версией Ubuntu

    rm -fr не работает

    Должен ли я использовать `mdadm -create` для восстановления моего RAID?

    Файлы Concat 1000 в каталоге

    Как сделать двойной загрузчик Kali Linux с ISO (без CD / USB / DVD)

    Сохранить вывод каждой команды для разделения файлов журналов?

    Linux и Unix - лучшая ОС в мире.