Цитирование и побег

У меня есть сценарий оболочки, аргументы которого собраны в кавычки как один аргумент и переданы в perl-скрипт.

/usr/local/API/check_api.sh "-D xxxx -C Test_Internal_Cluster -u user -p pass -i 300 -l runtime -s list" 

которые приводят к

 /usr/bin/perl /usr/local/check_api.pl -D xxxx -C Test_Internal_Cluster -u user -p pass -i 300 -l runtime -s list 

Я хочу привести такой аргумент:

 "Test Internal Cluster" 

к скрипту perl.

Когда я использую

 "Test\ Internal\ Cluster" 

как аргумент сценария оболочки, его разбор

 Test Internal Cluster 

для скрипта perl, но я хочу, чтобы это было в двойных кавычках.

Если я использую это:

 ""\"Test Internal Cluster\" 

Он преобразуется в:

'"Test' Internal 'Cluster"'

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

Скрипт оболочки:

 #!/bin/bash -xv Perl=/usr/bin/perl Api=/usr/local/API/check_api.pl if [ $# -gt 0 ]; then if [ -x $Api ] then output=$($Perl $Api $1) exitstatus=$? F1=$(echo $output | awk -F':' '{print $1}') F2=$(echo $output | awk -F':' '{print $2}' | awk -F'|' '{print $1}') F3=$(echo $output | awk -F':' '{print $2}' | awk -F'|' '{print $2}') echo " $F1: $F3 | $F2" else echo "check_api.pl not found" fi exit $exitstatus else echo "Usage : $0 '-D <DC> -C <Cluster> -u <userbame> -p <password> -i <interval> -l <command> -s <subcommand>'" fi 

Здесь вы можете увидеть, что весь аргумент, переданный сценарию оболочки, собран в $1 и передан в check_api perl.

Проблема происходит с именем кластера с пробелом (передается в -C).

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

Я не цитировал переменную $ 1, потому что perlscript не принимает весь аргумент как один, который ниже, который я нашел в режиме отладки, – где весь аргумент цитируется, если цитируется $ 1.

 /usr/bin/perl /usr/local/check_api.pl '-D xxxx -C Test_Internal_Cluster -u user -p pass -i 300 -l runtime -s list' 

и порождает ошибку как неправильное использование

Но без цитирования $ 1 аргумент, переданный perl-скрипту, работает так, и это дает мне результат:

 /usr/bin/perl /usr/local/check_api.pl -D xxxx -C Test_Internal_Cluster -u user -p pass -i 300 -l runtime -s list 

Но я потерпел неудачу с кластером с пространством от его имени.

В случае пробела в имени кластера, если я запускаю в командной строке напрямую (не из сценария оболочки), он отлично работает без каких-либо проблем:

 /usr/bin/perl /usr/local/check_api.pl -D xxxx -C "Test Internal Cluster" -u user -p pass -i 300 -l runtime -s list 

который я хотел получить из сценария оболочки.

Вы должны использовать вот так:

 "\"Test Internal Cluster\"" 

или оберните его в single quote :

 '"Test Internal Cluster"' 

Контрольная работа:

 #!/bin/bash echo $1 

Вывод:

 % bash test.sh "\"Test Internal Cluster\"" "Test Internal Cluster" 

Обновить

Проблема здесь в том, что вы называете perl с $1 , $1 содержит пробел, поэтому perl увидит это как три отдельных аргумента.

Чтобы этого избежать, вы должны обернуть $1 в двойные кавычки "$1" .

Еще одно замечание: вы должны обрабатывать аргумент $1 в perl-скрипте, потому что если $1 содержит какие-либо специальные символы, это вызовет некоторые ошибки в программе perl. вы можете использовать quotemeta($ARGV[0]) .

Заметка

Я думаю, что вы не передадите весь аргумент как строку в сценарий оболочки. Вы должны передать их как обычно, кроме имени кластера, вам все равно придется обернуть его как строку. Затем передайте их perl-скрипту "$@" вместо $1 .

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

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

Если вы запустите любую из следующих команд оболочки

 mycommand "foo bar" wibble mycommand 'foo bar' 'wibble' mycommand ''''foo" "bar \wib\ble mycommand foo\ bar \ "wibble" 

(или множество вариантов), то команда выполняется с точно такими же аргументами во всех случаях:

  • аргумент 0 – mycommand ;
  • Аргумент 1 – это 7-символьная строка foo bar ;
  • Аргумент 2 – это 6-символьная строка wibble .

Конструкция оболочки $1 не означает «принимать значение параметра 1». Это означает «принять значение параметра 1, разделить его на части, разделенные пробелами, и интерпретировать каждую часть как шаблон подстановочного знака и заменить ее на имена совпадающих файлов». Да, это довольно много. Как общий принцип программирования оболочки, всегда ставьте двойные кавычки вокруг переменных подстановок и команд susbtitutions : "$1" , "$some_variable" , "$(some_command)" .

Здесь два неправильных действия делают правильный, но только в узком случае: вы передаете один аргумент сценарию оболочки, а split разбивается на части, разделенные пробелами, чтобы превратить их в список строк, передаваемых в качестве аргументов в скрипт Perl. Это неинтуитивный и ограниченный интерфейс. Это не позволяет вам иметь пробелы в аргументах скрипта Perl, поскольку пробелы уже используются для разделения аргументов. Вы не можете иметь это в обоих направлениях. Цитаты здесь не помогают: вы можете передать символ цитаты в аргументе сценарию оболочки, и он станет частью аргумента для скрипта Perl. Например, если вы запустите один из

 check_api.sh "-C \"Test Internal Cluster\"" check_api.sh '-C "Test Internal Cluster"' check_api.sh -C\ \"Test Internal Cluster\" 

то сценарий оболочки вызывается с единственным аргументом -C "Test Internal Cluster" , а скрипт Perl вызывается с 4 аргументами -C , "Test , Internal и Cluster" .

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

 /usr/local/API/check_api.sh -D xxxx -C Test_Internal_Cluster -u user -p pass -i 300 -l runtime -s list /usr/local/API/check_api.sh -D xxxx -C 'Test Internal Cluster' -u user -p pass -i 300 -l runtime -s list /usr/local/API/check_api.sh -D xxxx -C "Test Internal Cluster" -u user -p pass -i 300 -l runtime -s list 

(последние два эквивалентны, поскольку одиночные и двойные кавычки имеют значение только в том случае, когда \$` появляются внутри7) и исправляют сценарий оболочки для использования двойных кавычек вокруг подстановок команд.

 if [ -x "$Api" ] then output=$("$Perl" "$Api" "$1") exitstatus=$? F1="${output%%:*}"; output="${output#*:}" output="${output%%:*}" F2="${output%%\|*}"; output="${output#*\|}" F3="${output%%\|*}" fi 

Я заменил awk-код эквивалентными конструкциями оболочек, считая, что вывод является одной строкой.

Проблема заключается в следующей строке:

 output=$($Perl $Api $1) 

Вы должны указать аргумент:

 output=$($Perl $Api "$1") 

Когда вы не приводите эту переменную, слово-расщепление вызывает perl, чтобы увидеть несколько аргументов.

На самом деле проблема заключается в том, что вы должны правильно выполнять парсинг. Похоже, что вы не знаете, как это сделать, но в этом нет никакого стыда.

Используя встроенный синтаксический анализ параметров bash и правильное цитирование их по пути, все это будет работать так, как вы этого хотите, что означает, что вы можете обычно указывать его в командной строке. Нет необходимости повторно цитировать или избегать чего-либо.

 #!/bin/bash PERL=/usr/bin/perl API=/usr/local/API/check_api.pl usage() { echo "Usage : $0 -D <DC> -C <Cluster> -u <userbame> -p <password> -i <interval> -l <command> -s <subcommand>" exit 1 } while getopts "C:D:u:p:i:l:s:" options; do case $options in C ) CLUSTER="${OPTARG}";; D ) DC="${OPTARG}";; u ) USER="${OPTARG}";; p ) PASS="${OPTARG}";; i ) INTERVAL="${OPTARG}";; l ) COMMAND="${OPTARG}";; s ) SUBCOMM="${OPTARG}";; * ) usage ;; esac done $PERL $API -C "$CLUSTER" -D "$DC" -u "$USER" -p "$PASS" -i "$INTERVAL" -l "$COMMAND" -s "$SUBCOMM" 

Похоже, что вы делаете некоторые другие вещи с выходом, которые я оставлю вам, чтобы добавить к этому примеру.

Проблема в том, что вы пытаетесь использовать одну переменную для хранения списка строк:

У меня есть сценарий оболочки, аргументы которого собраны в кавычки как один аргумент и переданы в perl-скрипт.

Это не работает. После того, как вы объединяете несколько аргументов и получаете одну строку, различие между пробелами внутри аргументов и пробелами между аргументами теряется. Невозможно исправить это, включив цитирование символов внутри строки, потому что, просто говоря, цитирующие символы теряют свою магию как вскоре, поскольку они являются частью строки. Обратная косая черта или " внутри строки – обычный символ, она ничего не цитирует.

Решение. Используйте переменную массива для сбора аргументов:

 A=() A+=(-D "$address") A+=(-C "$clustername") A+=(-u "$username") A+=(-p "$password") ... output=$("$Perl" "$Api" "${A[@]}") 

(отметьте цитату.)

Конечно, если аргументы, которые вы хотите передать в программу perl, – это всего лишь аргументы командной строки, которые передаются вашему скрипту, тогда они уже содержатся в массиве (а именно в списке аргументов командной строки "$@" ) , поэтому вы можете просто использовать его:

 output=$("$Perl" "$Api" "$@")