У меня есть файл 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
ssh
scripting
io-redirection
Мартин Вегтер
источник
источник
ansible
Ответы:
ssh
читает остальную часть вашего стандартного ввода.read
читает со стандартного ввода. В<
перенаправляет стандартный ввод из файла.К сожалению, команда, которую вы пытаетесь запустить, также читает stdin, поэтому она в итоге съедает остаток вашего файла. Вы можете увидеть это ясно с:
Обратите внимание, как
cat
съели (и повторили) оставшиеся две строки. (Если бы read сделал это, как ожидалось, каждая строка имела бы «начало» и «конец» вокруг хоста.)Почему
for
работает?Ваша
for
линия не перенаправляет на стандартный ввод. (На самом деле, он читает все содержимоеservers.txt
файла в память перед первой итерацией). Так чтоssh
продолжает читать его стандартный вывод из терминала (или, возможно, ничего, в зависимости от того, как называется ваш скрипт).Решение
По крайней мере, в bash вы можете
read
использовать другой файловый дескриптор.должен работать.
10
это просто произвольный номер файла, который я выбрал. 0, 1 и 2 имеют определенные значения, и обычно открываемые файлы начинаются с первого доступного числа (поэтому 3 будет использоваться в дальнейшем). 10, таким образом, достаточно высока, чтобы не мешать, но достаточно низка, чтобы находиться в пределах некоторых оболочек. Плюс это хороший круглый номер ...Альтернативное решение 1: -n
Как указывает Макнисс в своем ответе , у клиента OpenSSH есть
-n
опция, которая не позволяет ему читать stdin. Это хорошо работает в конкретном случаеssh
, но, конечно, в других командах этого может не быть - другие решения работают независимо от того, какая команда потребляет ваш стандартный ввод.Альтернативное решение 2: второе перенаправление
Видимо, вы можете (как я уже пробовал, это работает в моей версии Bash, по крайней мере ...) сделать второе перенаправление, которое выглядит примерно так:
Вы можете использовать это с любой командой, но это будет трудно, если вы действительно хотите, чтобы ввод терминала передавался команде.
источник
10
?exec 3<&0; while read HOST; do ssh $HOST "uname -a" <&3; done <servers.txt; exec 3<&-
это делает файловый дескриптор 3 резервной копией исходного stdin, затем использует его для sd sdin, а затем закрывает резервную копию FD, когда это будет сделано. Это работает на любой POSIX-совместимой оболочке.-u
опция не поддерживается POSIX и поэтому не должна использоваться для#!/bin/sh
сценариев; используйтеread HOST <&10
вместо Кроме того, POSIX требует, чтобы оболочки поддерживали файловые дескрипторы от 0 до 9, поэтому10<servers.txt
не могут использоваться, если сценарий должен строго соответствовать.Как описывает Дероберт,
ssh
читаетstdin
.Чтобы изменить это поведение, вы можете добавить -n no ssh, чтобы он не читал стандартный ввод.
источник
Возможно, вам лучше использовать pssh из проекта Parallels-SSH .
-i
означает интерактивный. pssh также поставляется с параллельным scp и параллельным rsync. Что приятно, так это то, что он работает асинхронно и будет запускать столько потоков, сколько вы просите. По умолчанию (не -i / интерактивный) выводится в отдельные каталоги для stdout / stderr, что делается с помощью $ outputdir / $ hostname.источник
Если вы обнаружите, что делаете это довольно часто, вам следует попробовать Fabric
Установите Fabric, следуя инструкциям , в большинстве случаев вам просто нужно
sudo apt-get install fabric
Создайте файл с именем
fabfile.py
следующего кода:Затем запустить
fab mytask
даст вам результат, который вы хотите.источник
Проще использовать такую команду:
Я обычно делаю так:
echo
, Чтобы увидеть , какой сервер застрял, или не может подключиться к нему.источник
Потому что ssh commnand берет весь поток из стандартного ввода, подаваемого оператором while,
Вы можете использовать канал для переключения stdin из ssh на другой источник:
пример :
Ввод stdin всех команд ssh в цикле while должен быть переключен на другой источник.
источник