Используя цикл с условием продолжения к ssh к нескольким серверам

У меня есть файл servers.txt, со списком серверов:

server1.mydomain.com
server2.mydomain.com
server3.mydomain.com

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

$ while read HOST ; do echo $HOST ; done < servers.txt
server1.mydomain.com
server2.mydomain.com
server3.mydomain.com

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

$ while read HOST ; do ssh $HOST "uname -a" ; done < servers.txt
Linux server1 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux

Это только соединяется с первым сервером в списке, не всем им. Я не понимаю то, что происходит здесь. Кто-то может объяснить?

Это еще более странно, начиная с использования for цикл хорошо работает:

$ for HOST in $(cat servers.txt ) ; do ssh $HOST "uname -a" ; done
Linux server1 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux
Linux server2 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux
Linux server3 2.6.30.4-1 #1 SMP Wed Aug 12 19:55:12 EDT 2009 i686 GNU/Linux

Это должно быть что-то характерное для ssh, потому что другие команды хорошо работают, такой как ping:

$ while read HOST ; do ping -c 1 $HOST ; done < servers.txt
77
05.01.2014, 01:46
5 ответов

ssh читает остальную часть Вашего стандартного входа.

while read HOST ; do … ; done < servers.txt

read чтения от stdin. < перенаправления stdin из файла.

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

$ while read HOST ; do echo start $HOST end; cat; done < servers.txt 
start server1.mydomain.com end
server2.mydomain.com
server3.mydomain.com

Заметьте как cat поел (и отозвался эхом), оставление двумя строками. (Читал сделанный это как ожидалось, каждая строка будет иметь "запуск" и "конец" вокруг хоста.)

Почему делает for работа?

Ваш for строка не перенаправляет к stdin. (На самом деле это читает все содержание servers.txt файл в память перед первым повторением). Так ssh продолжает читать его stdin из терминала (или возможно ничто, в зависимости от того, как Ваш сценарий называют).

Решение

По крайней мере, в ударе, Вы можете иметь read используйте другой дескриптор файла.

while read -u10 HOST ; do ssh $HOST "uname -a" ; done 10< servers.txt
#          ^^^^                                       ^^

должен работать. 10 просто произвольный номер документа, который я выбрал. 0, 1, и 2 определили значения, и обычно вводные файлы начнут с первого доступного числа (таким образом, 3 будет рядом с использоваться). 10 таким образом достаточно высоко, чтобы остаться вне пути, но достаточно низко находиться под пределом в некоторых оболочках. Плюс он - круглое число...

Альтернативное решение 1:-n

Как McNisse указывает в его ответе, клиент OpenSSH имеет -n опция это будет препятствовать тому, чтобы он читал stdin. Это работает хорошо в особом случае ssh, но конечно другие команды могут испытать недостаток в этом — другая работа решений, независимо от которой команда ест Ваш stdin.

Альтернативное решение 2: второе перенаправление

Вы можете, по-видимому (как в, я попробовал его, это работает в моей версии Bash, по крайней мере...), делают второе перенаправление, которое выглядит примерно так:

while read HOST ; do ssh $HOST "uname -a" < /dev/null; done < servers.txt

Можно использовать это с любой командой, но это будет трудно, если Вы на самом деле захотите терминальный вход, идущий в команду.

98
27.01.2020, 19:31
  • 1
    , где делает число 10 произойти из? –  Martin Vegter 04.01.2014, 10:02
  • 2
    @MartinVegter я составил его. 0/1/2 являются stdin, stdout, и stderr. Можно выбрать любое число, которое Вы любите — удар позволяет Вам пойти довольно высоко, вероятно, до предела ОС. Другие оболочки могут ограничить Вас меньше... –  derobert 04.01.2014, 10:20
  • 3
    @MartinVegter я отредактировал для ответа на оба из комментариев. –  derobert 04.01.2014, 10:25
  • 4
    Другая альтернатива: exec 3<&0; while read HOST; do ssh $HOST "uname -a" <&3; done <servers.txt; exec 3<&- Это делает дескриптор файла 3 резервное копирование исходного stdin, затем использует его для stdin ssh, затем закрывает резервный FD при выполнении. Это работает над любой совместимой с POSIX оболочкой. @DougO' Neal –  Richard Hansen 08.01.2014, 20:49
  • 5
    -u опция не поддерживается POSIX и таким образом не должна использоваться для #!/bin/sh сценарии; использовать read HOST <&10 вместо этого. Кроме того, POSIX только требует оболочек к дескрипторам файла поддержки 0 до 9, таким образом, 10<servers.txt не может использоваться, если сценарий должен строго соответствовать. –  Richard Hansen 08.01.2014, 20:58

Поскольку derobert описывает ssh чтения Ваш stdin.

Для изменения этого поведения, можно добавить-n никакой ssh, чтобы препятствовать тому, чтобы он читал stdin.

ssh -n $HOST "uname -a"
31
27.01.2020, 19:31

Вы, вероятно, на самом деле были бы более обеспеченным использованием pssh из параллельного-ssh проекта.

pssh -h $hostfile -t $timeout -i $commands

-i интерактивные средства. pssh также идет с параллелью scp и параллелью rsync. То, что хорошо, - то, что это работает асинхронно и выполнит столько потоков, сколько Вы спрашиваете это к. Значение по умолчанию (не-i/interactive) должно произвести для разделения каталогов для stdout/stderr, который оно делает $outputdir/$hostname.

10
27.01.2020, 19:31

При находке этого вида задачи довольно часто необходимо попробовать Матрицу

Установите Матрицу, следующую инструкциям, большинство случаев, в которых Вы просто нуждаетесь sudo apt-get install fabric

Создайте названный файл fabfile.py со следующим кодом:

from fabric.api import env, run

env.hosts = ['server1.mydomain.com',
             'server1.mydomain.com',
             'server1.mydomain.com']

def mytask():
    run('uname -a')

Затем выполненный fab mytask даст Вам результат, который Вы хотите.

3
27.01.2020, 19:31

Это - более легкое использование команды как это:

for f in `cat servers.txt`; do ssh $f uname -a; done

Мне обычно нравится это:

for f in `cat servers.txt`; do echo "### $f ###"; ssh $f uname -a; done

echo должен видеть, какой сервер застревает или не может соединиться с ним.

1
27.01.2020, 19:31

Теги

Похожие вопросы