Bash: перебирання на збережені імена хостів, виконання команд через SSH на цих хостах


0

Я зберігаю у файлі з назвою ssh_hosts.txt список імен хостів. У сценарії оболонки я перебираю імена хостів у ssh_hosts.txt і виконую команду на названому хості через SSH. Сценарій оболонки показаний нижче.

Проблема полягає в тому, що скрипт виходить після обробки першого хоста. Але якщо я виконую щось інше, ніж виконувати команду на названому хості через ssh, скрипт запуститься до завершення.

У наведеному нижче прикладі я прокоментував заклик до ssh і замінив його простим відлунням поточного імені хоста. Це триватиме до завершення.

Я виконую цей скрипт з bash оболонки, що працює під наступною версією Cygwin в Windows 7:

$ uname -a
CYGWIN_NT-6.1 myHostname 1.7.16(0.262/5/3) 2012-07-20 22:55 i686 Cygwin

Ці версії SSH задіяні:

$ ssh -V
OpenSSH_6.0p1, OpenSSL 1.0.1c 10 May 2012

$ ssh myUsername@remoteHost 'ssh -V'
myUsername@remoteHost password:
OpenSSH_6.7p1 Debian-5+deb8u3, OpenSSL 1.0.1t  3 May 2016

Ось сценарій оболонки:

#!/bin/bash

if [[ $# -ne 1 ]]; then
   echo "Usage: $(basename $0) <user name>"
   exit 1
fi

USER="$1"

while IFS='' read -r ssh_host || [[ -n "$ssh_host" ]];
do
   # This line will execute for all hosts listed in ssh_hosts.txt.
   echo $ssh_host

   # This line will execute for *only the first* host in ssh_hosts.txt.
   # ssh $USER@$ssh_host 'echo $(whoami)@$(hostname)'
done < ssh_hosts.txt

Як я можу змусити цей скрипт оболонки виконати над усіма хостами в ssh_hosts.txt, а не над першим хостом?


Відповіді:


2

Я думаю, що ти робиш це занадто складно:

for ssh_host in $(cat ssh_hosts.txt)
do
   echo $ssh_host
   ssh $user@$ssh_host ....
done

Крім того, шукайте github для gsh, що є програмою Perl, яка зробить більше, якщо ви робите це часто.


Мені б хотілося, щоб усі запропоновані відповіді обрали як "відповідь" - всі вони були дуже гарні, і я впевнений, що я ціную час людей, щоб їх запропонувати ..., але в кінцевому підсумку я вибрав цю, тому що це найбільше просто і безпосередньо вирішило мою конкретна проблема. Дякуємо dave_thompson_085, virtex, dgoo2308 та eggo!
Дейв

3

У вас у програмі дві програми, що читають зі stdin: read та ssh. Коли ваш цикл запускається, команда read прочитає перший рядок із ssh_hosts.txt, а потім ssh прошиває решту. Тому ви бачите лише підключення до першого хоста.

Я знаю два рішення цієї проблеми. Одне - переписати цикл, щоб він не переспрямовувався з stdin. Хороший спосіб зробити це за допомогою forпетлі, яку Егго має на своєму посту.

Іншим рішенням є передати цей -nваріант ssh:

ssh -n $USER@$ssh_host 'echo $(whoami)@$(hostname)'

Це говорить ssh не використовувати stdin, залишаючи повний вміст вашого ssh_hosts.txt команді read. Це рішення вимагає, щоб ваші сеанси ssh не вимагали жодної взаємодії з користувачем, включаючи запит на введення пароля (тому використовуйте ключі ssh для автентифікації).


Ах-а-стдін, дякую за пояснення основної проблеми!
Дейв

2

Вам потрібно розщедритися, оскільки ssh зіпсує вашу петлю.

У мене в руках скрипт для оновлення авторизованих ключів, я робив це за допомогою 2-х сценаріїв:

  • run.sh: прочитайте список хостів та роздрібніть do_command.shскрипт
  • do_command.sh: встановити з'єднання з керуючим сокетом, виконувати команди та закрити

Як додатковий бонус:

  • всі хости підключені одночасно
  • кілька команд виконуються одним з'єднанням
  • файл журналу з результатами

Основний сценарій: run.sh

#!/bin/bash
TIMEOUT=60
COUNT=0
rm -rf log
mkdir log
while read -u10 HOST ; do ( ./do_command.sh $HOST & ); done 10< ssh_hosts.txt
sleep 1
for f in log/*.pid; do
    [ -e "$f" ] && VMN=`ls log/*.pid`
    echo "waiting for process to finish"
    break
done
while [ "$VMN" != "" ]
do

  sleep 3
  COUNT=$(($COUNT+3))
  if (("$COUNT" != "$TIMEOUT"))
    then
        VMN=""
        for f in log/*.pid; do
            [ -e "$f" ] && VMN=`ls log/*.pid`
            break
        done
        if [ "$VMN" != "" ]
        then
            echo "waiting already "$COUNT" seconds ..."
            #for VM in $VMN; do 
                #myPID=$(<"$VM")
                #echo -e "\t pid:$myPID \t $VM "

                #done
        fi
    else
        echo -e "\n!!! timeout, waiting for remaining processes's to shutdown"
        for VM in $VMN; do
            myPID=$(<"$VM")
            ITEM=$(sed -e 's/.pid//;s/log\///' <<<"$VM")
            LOGX="log/$ITEM.log"
            echo -e "killing $myPID -> item: $ITEM \t log: $LOGX" 
            kill -9 $myPID 
            rm -rf $VM
            echo -e "\n ###### main Process ###### \n\n \t Killed due to Time out \nFAILED $ITEM" >>$LOGX
            echo "$ITEM Killed due to Time out" >>log/error.txt
        done
        rm -fv log/*.sock
        VMN=''
    fi
done
if [ -f log/error.txt ] 
then
    rm -fv log/*.sock
    echo -e "\n##### FAIL ##############\n"
    cat log/error.txt
    echo
    exit 1
fi
echo

Сценарій дії: do_command.sh

#!/bin/bash
exec 5<&1 
exec 6<&2
echo "PROCESS $1"
myPID=$$
echo $$ >"log/$1.pid"
chmod 777 "log/$1.pid"
exec 1> "log/$1.log" 2>&1
echo "PROCESS $1"
SOCKET="log/$1_$myPID.sock"
TARGET="root@$1"
echo "Connecting to $TARGET .... "
ssh -f -N -M -o ControlPath=$SOCKET $TARGET
if [ $? -ne 0 ]; then
    echo "Could not connect $1, Cleanup "
    echo "Could not connect $1, exit " >>log/error.txt
    #ssh -S $SOCKET -O exit $TARGET
    rm -fv "log/$1.pid"
    rm -fv $SOCKET
    echo "FAILED $TARGET">&6
    echo "FAILED $TARGET"
    exit 1
fi

# Do the thing you need to do
# This script copy a file to the remote and executes 2 commands
#
scp -o ControlPath=$SOCKET authorized_keys $TARGET:.ssh/new_authorized_keys

# Execute a command:
ssh -o ControlPath=$SOCKET $TARGET "mv -fv .ssh/authorized_keys .ssh/old_authorized_keys && cp -fv .ssh/new_authorized_keys .ssh/authorized_keys && chmod 600 .ssh/authorized_keys"

# Another remote command:   
ssh -o ControlPath=$SOCKET $TARGET "ls -la .ssh"

# And Close the socket 
ssh -S $SOCKET -O exit $TARGET

sleep 1
echo Cleanup
rm -fv "log/$1.pid"
rm -fv $SOCKET
echo "SUCCESS $TARGET"
echo "SUCCESS $TARGET">&5
exit 0

Дуже приємне рішення, і так, проблема, яку я намагаюся вирішити, - це творче ефективне управління SSH-ключем (як хост, так і користувацькі ключі).
Дейв

@Dave не забудьте позначити як відповів
Danny Goossen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.