2013-10-24 3 views
0

У меня есть простой скрипт bash для запуска удаленной команды на заданном наборе серверов.Баш-аргументы дословной интерпретации

#!/bin/bash 

echo "Command to be run:" 
echo "$*" 
read nothing 

servers="server1 server2 server3" 

for server in `echo $servers` 
do 
    echo $server 
    ssh $server "$*" 
    echo "" 
done 

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

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

пример с использованием «@»:

./cmd tw_query --no-headings "search Host where name matches '(?i)peter' show summary, nodecount(traverse :::Detail where name matches 'bob')" 

Дает:

Command to be run: 
tw_query --no-headings search Host where name matches '(?i)peter' show summary, nodecount(traverse :::Detail where name matches 'bob') 
+2

Используйте '' $ @ "' вместо '' $ * "' Это должно решить ваши проблемы. –

+3

Вы можете заменить 'для сервера в' echo $ servers' '' 'для сервера в $ servers' – damienfrancois

+0

Передача строки, подобной этой' ssh', сложна, потому что ваша строка анализируется один раз перед вызовом вашего скрипта и установкой '$ * 'и снова, когда строка получена на удаленном конце. Обычно проще просто написать сценарий, скопировать его на удаленный хост и выполнить удаленный скрипт через 'ssh'. – chepner

ответ

2

Вы, кажется, ищете [email protected]. Скажем:

ssh $server "[email protected]" 

вместо этого. Из manual:

*

Заменяется позиционными параметрами, начиная с первого. Когда расширение происходит в двойных кавычках, оно расширяется до одного слова со значением каждого параметра, разделенным первым символом специальной переменной IFS. То есть "$*" эквивалентен "$1c$2c…", где c - первый символ значения переменной IFS. Если IFS не задано, параметры разделяются пробелами. Если IFS имеет значение null, , параметры соединяются без промежуточных разделителей.

@

Раскрывается в позиционные параметры, начиная с первого. Когда расширение происходит в двойных кавычках, каждый параметр расширяется до отдельного слова . То есть "[email protected]" эквивалентен "$1" "$2" …. Если в одном слове происходит двойное кавычки , расширение первого параметра соединяется с начальной частью исходного слова , а расширение последнего параметра соединяется с последней частью исходного слова . Когда нет позиционных параметров, "[email protected]" и [email protected] развернуть до нуля (т. Е. Они удаляются).

+0

Пробовал это, см. Править. – delronhubbard

0

Помимо проблемы с $* против [email protected], если это для использования в производственной среде, вы можете рассмотреть такой инструмент, как pdsh.

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

#!/bin/bash 

read cmd 
echo "Command to be run:" 
echo $cmd 
servers="server1 server2 server3" 

for server in `echo $servers` do 
    echo $server 
    ssh $server "$cmd" 
    echo "" 
done 

и использовать его как этот

$ ./cmd <<'EOT' 
> tw_query --no-headings "search Host where name matches '(?i)peter' show summary,  nodecount(traverse :::Detail where name matches 'bob')" 
> EOT 
Command to be run: 
tw_query --no-headings "search Host where name matches '(?i)peter' show summary, nodecount(traverse :::Detail where name matches 'bob')" 

Может быть немного надуманным, но она могла бы работать.

+0

Спасибо, но установка дополнительного программного обеспечения на этом приложении ОС, ну, в лучшем случае нахмурилась! Спасибо за предложение! ;) – delronhubbard

1

На самом деле вы не хотите, чтобы аргументы, переданные удаленному серверу, были повреждены, вы хотите, чтобы они прошли удаленной команде. Но это означает, что они должны быть завернуты в дополнительный слой кавычек/escapes/etc, так что они останутся нетронутыми после того, как удаленная оболочка проанализировала их.

bash на самом деле имеет функцию в своем встроенном printf, чтобы добавить цитату/экранирование в строку, но она подходит для интерпретации самим bash - если удаленная оболочка была чем-то другим, она может не понимать режим цитирования, выбирает. Поэтому в этом случае я бы рекомендовал простой и немой тип цитирования: просто добавьте одиночные кавычки вокруг каждого аргумента и замените каждую отдельную кавычку в аргументе '\'' (это приведет к завершению текущей цитируемой строки, добавит экранированную строку (буквальная), затем запустите другую строку с кавычками). Это будет немного странно, но должно нормально декодироваться под любой POSIX-совместимой оболочкой.

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

#!/bin/bash 

quotedquote="'\''" 
printf -v quotedcommand "'%s' " "${@//\'/$quotedquote}" 
echo "Command to be run:" 
echo "$quotedcommand" 
read nothing 

servers="server1 server2 server3" 

for server in $servers 
do 
    echo $server 
    ssh $server "$quotedcommand" 
    echo "" 
done 

А вот как это приводит свой пример команды:

'tw_query' '--no-headings' 'search Host where name matches '\''(?i)peter'\'' show summary, nodecount(traverse :::Detail where name matches '\''bob'\'')' 

Это выглядит странно, чтобы иметь команду сам цитировал, но до тех пор, пока вы не пытаетесь используйте псевдоним, который не вызывает никаких реальных проблем. Существует одно существенное ограничение, хотя: нет никакого способа, чтобы передать оболочки метасимволы (например, > для перенаправления вывода) к удаленной оболочки:

./cmd somecommand >outfile # redirect is done on local computer 
./cmd somecommand '>outfile' # ">outfile" is passed to somecommand as an argument 

Если вам нужно сделать что-то вроде удаленных переадресовывает, вещи получают намного больше сложно.

+0

У вас есть двойной эквивалент, которого вы не хотите, и вы могли бы использовать 'printf -v' там вместо' = 'назначения в любом случае. – kojiro

+0

@kojiro «D'oh (error-paste error)» и «good point», соответственно. Под редакцией ... –

Смежные вопросы