4

Фон

Я создал скрипт для:

  • Читайте список IP по одному за раз
  • Скопируйте файл конфигурации с локального хоста на удаленный хост
  • Перезагрузите удаленный хост
  • Закрыть текущий сеанс SSH

Содержание сценария:

#!/bin/bash

SSHPASS="OMITTED"
FILE_TO_COPY=/opt/someConfigFile.conf
TARGET_FOLDER=/opt/

echo "Reading ip list..."
cat $1 | while read ip
do
    echo "Copying file to $ip"
    sshpass -p $SSHPASS scp -o StrictHostKeyChecking=no -o ServerAliveInterval=3 $FILE_TO_COPY root@$ip:$TARGET_FOLDER
    echo "Sending reboot command to $ip"
    sshpass -p $SSHPASS ssh -o StrictHostKeyChecking=no -o ServerAliveInterval=3 'nohup reboot > /dev/null & exit'
    echo "Done for $ip"
done
echo "Done for all"

Мой скрипт читает записи из текстового файла, где каждая запись разделена новой строкой, что-то вроде:

192.168.XXX.XX1
192.168.XXX.XX2
192.168.XXX.XX3
192.168.XXX.XX4

Когда я запускаю этот ./ConfigSender.sh /host_list.txt Я вижу следующий вывод:

$> ./ConfigSender.sh /host_list.txt
Rading ip list...
Copying file to 192.168.XXX.XX1
Sending reboot command to 192.168.XXX.XX1
Done for 192.168.XXX.XX1
Done for all
$>

Я ожидал увидеть вывод для всех записей в файле. Я подозревал, что команды внутри while как-то нарушали исполнение. Поэтому я отредактировал свой скрипт так, чтобы он просто выводил значение read ip для вывода:

#...
echo "Reading ip list..."
cat $1 | while read ip
do
    echo "Copying file to $ip"
done
echo "Done for all"

Это вывод:

$> ./ConfigSender.sh /host_list.txt
Rading ip list...
Copying file to 192.168.XXX.XX1
Copying file to 192.168.XXX.XX2
Copying file to 192.168.XXX.XX3
Copying file to 192.168.XXX.XX4
Done for all
$>

Вопрос

Понятно, что команды внутри оригинала while вызывают это поведение. Что не так с циклом while? Что мне здесь не хватает?

РЕДАКТИРОВАТЬ

Оба ответа @harrymc & @Kamil Maciorowski решают мою проблему, большое спасибо. Я решил принять ответ @ harrymc, так как он более наглядный.

3 ответа3

3

sshpass работает, манипулируя stdin чтобы обмануть ssh заставляя его думать, что он получает пароль от интерактивного пользователя. Когда вы используете ... | while Цикл while style, цикл повторяется для каждой строки, поступающей из stdin , которую sshpass стирает после первого вызова. Вот почему выполняется только первая строка.

Возможны несколько решений:

  • Перенаправление для sshpass стандартного ввода в /dev/null
  • Оберните все тело цикла в фигурные скобки, чтобы изолировать стандартный ввод ({})
  • Назначить массив перед циклом, чтобы вы не использовали стандартный ввод
    readarray a < file
    for ip in "${a[@]}"; do
  • Цикл по файловому дескриптору, отличному от стандартного ввода
    while read -u 5 -r ip; do
    ...
    done 5<file
2

Я сделал несколько тестов. Я думаю, что ваши строки как

sshpass …

читайте stdin , они "кушают" дополнительные IP-адреса, предоставленные cat . Вы можете выяснить , почему это происходит , но этого достаточно , чтобы знать , что это произойдет.

Решение состоит в том, чтобы дать им некоторый другой вклад:

</dev/null sshpass …

Или вы можете перестроить свой цикл следующим образом:

for ip in `cat $1`
do
 …
done
0

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

sshpass -p $SSHPASS ssh -o StrictHostKeyChecking=no -o ServerAliveInterval=3 root@$ip 'nohup reboot > /dev/null & exit'

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .