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

Я ежедневно работаю с определенной клиентской средой, состоящей из 5 серверов AIX, и иногда мне нужно выполнить одну и ту же команду на всех 5 из них.

Поэтому я установил аутентификацию между серверами на основе ключей SSH и создал небольшой ksh-скрипт, который транслирует команду всем из них:

#!/usr/bin/ksh

if [[ $# -eq 0 ]]; then
        print "broadcast.ksh - broadcasts a command to the 5 XXXXXXXX environments and returns output for each"
        print "usage:                   ./broadcast.ksh command_to_issue"
        exit
fi

set -A CUST_HOSTS aaa bbb ccc ddd eee

for host in ${CUST_HOSTS[@]}; do

        echo "============ $host ================"

        if [[ `uname -n` = $host ]]; then
                $*
                continue
        fi

        ssh $host $*

done

echo "========================================="
echo "Finished"

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

./broadcast.ksh ls -l java*

поскольку '*' расширяется в локальной системе, а не в удаленной.

Теперь, используя удаленные команды ssh, я могу обойти это, используя одинарные кавычки:

ssh user@host ls -l java*    <-- will _not_ work as expected, since asterisk will be interpreted locally
ssh user@host 'ls -l java*'   <-- _will_ work as expected, since asterisk will be interpreted on the remote end

Теперь я попытался включить это в свой сценарий и попытался создать переменную $ command, состоящую из содержимого $ *, окруженного одинарными кавычками, но утонул в море экранирования обратных косых черт и попыток конкатенации в ksh, чтобы не было польза

Я уверен, что есть простое решение, но я не нашел его, поэтому подумал, что выйду и спрошу.

Спасибо,

Джеймс


person James    schedule 06.03.2014    source источник
comment
Подойдут ли двойные кавычки лучше? AFAIK они сохранят подстановочный знак, но все равно расширят $.   -  person Holloway    schedule 06.03.2014
comment
Спасибо, @Trengot, но это не так. Это было среди вещей, которые я тестировал, но я пошел дальше и на всякий случай перепроверил. Он по-прежнему интерпретирует звездочку в локальной системе.   -  person James    schedule 06.03.2014
comment
@James Это, вероятно, не ответит на ваш вопрос, но может дать вам другую идею, кроме создания собственного скрипта с нуля. Есть инструмент под названием dsh , который является оболочкой для выполнения нескольких команд оболочки на удаленных хостах. Также вы можете проверить этот сайт < / b> - хороший пример использования параллельного SSH.   -  person jimm-cl    schedule 06.03.2014
comment
@jim спасибо за предложения. Звучит dsh, он делает то, что мне нужно ... Интересно, как он обрабатывает подстановочные знаки, лол. Но я не могу ничего установить в этой клиентской среде, поэтому мне нужно исправить мой скрипт, если это вообще возможно. Статья IBM выглядит очень многообещающей ... Я обязательно ее прочту, возможно, ответ на мой вопрос где-то там.   -  person James    schedule 06.03.2014


Ответы (2)


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

for host in ${CUST_HOSTS[@]}; do
    echo "============ $host ================"
    if [[ `uname -n` = $host ]]; then
        "$*"
        continue
    fi
    ssh $host "$*"
done

$ ./broadcast.ksh ls -l java\*

$ ./broadcast.ksh 'ls -l java*; ls -l *log'
person Josh Jolly    schedule 06.03.2014
comment
Спасибо за предложение, Джош, но ни один из них не сработал. Звездочка по-прежнему интерпретируется в локальной системе. - person James; 06.03.2014
comment
@James Обходной путь мог бы избежать использования подстановочных знаков ... Я знаю, я знаю, это совсем не элегантно :-p Что-то вроде $ ./broadcast.ksh 'ls -l | grep ^java' должно работать, я считаю. - person jimm-cl; 06.03.2014
comment
Да, @jim, это не то, на что я надеялся, когда решил передать это сообществу SO ... но я должен признать некоторые достоинства этой идеи. :-) - person James; 07.03.2014

Я хотел прокомментировать, но все еще слишком мало, но предложение Джоша с одинарной цитатой должно работать.

  • Я создал пару виртуальных машин с двумя файлами в / tmp: / tmp / foo1 и / tmp / foo2.
  • затем использовал вариант вашего скрипта
root@jdsdrop1:~# cat foo.sh
#!/usr/bin/ksh

if [[ $# -eq 0 ]]; then
        print "broadcast.ksh - broadcasts a command to the 5 XXXXXXXX environments and returns output for each"
        print "usage:                   ./broadcast.ksh command_to_issue"
        exit
fi

set -A CUST_HOSTS jdsdropfed1 jdsdropfed2-2

for host in ${CUST_HOSTS[@]}; do

        echo "============ $host ================"

        if [[ `uname -n` = $host ]]; then
                $*
                continue
        fi

        ssh $host $*

done

echo "========================================="
echo "Finished"
root@jdsdrop1:~# ./foo.sh 'ls /tmp/foo*'
============ jdsdropfed1 ================
/tmp/foo1
/tmp/foo2
============ jdsdropfed2-2 ================
/tmp/foo1
/tmp/foo2
=========================================
Finished
root@jdsdrop1:~# ssh jdsdropfed1 "ls /tmp/foo*"
/tmp/foo1
/tmp/foo2
root@jdsdrop1:~# ssh jdsdropfed2-2. "ls /tmp/foo*"
/tmp/foo1
/tmp/foo2
person JimSander    schedule 18.04.2017