while 루프를 사용하여 여러 서버에 ssh


75

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

4
사용ansible
Matt

답변:


98

ssh 나머지 표준 입력을 읽고 있습니다.

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

readstdin에서 읽습니다. <리디렉션 파일에서 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라인은 표준 입력 리디렉션하지 않습니다. 실제로 servers.txt파일 의 전체 내용을 첫 번째 반복 전에 메모리로 읽습니다 . 따라서 ssh터미널에서 stdin을 계속 읽습니다 (또는 스크립트 호출 방법에 따라 아무것도 아님).

해결책

적어도 bash에서는 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표준 입력을 읽는 것을 방지합니다 옵션을 선택합니다. 이것은 특별한 경우에는 잘 작동 ssh하지만 물론 다른 명령에는 이것이 없을 수 있습니다. 다른 솔루션은 stdin을 먹는 명령에 관계없이 작동합니다.

대체 솔루션 2 : 두 번째 리디렉션

분명히 (내가 시도한 것처럼 내 Bash 버전에서 적어도 작동합니다 ...) 두 번째 리디렉션을 수행하면 다음과 같습니다.

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

어떤 명령으로도 사용할 수 있지만 실제로 터미널 입력을 명령으로 보내려면 어려울 수 있습니다.


1
번호는 10어디에서 왔습니까?
Martin Vegter

2
@MartinVegter 내가 만들었습니다. 0/1/2는 stdin, stdout 및 stderr입니다. 원하는 숫자를 선택할 수 있습니다. bash를 사용하면 OS 한도까지 올라갈 수 있습니다. 다른 포탄은 당신을 더 적게 제한 할 수 있습니다 ...
derobert

@MartinVegter 두 의견에 모두 답변하도록 편집했습니다.
derobert

다른 대안 : exec 3<&0; while read HOST; do ssh $HOST "uname -a" <&3; done <servers.txt; exec 3<&- 파일 디스크립터 3을 원래 stdin의 백업으로 만든 다음 ssh의 stdin에 사용하고 완료되면 백업 FD를 닫습니다. 이것은 POSIX 호환 쉘에서 작동합니다.
Richard Hansen

8
-u옵션은 POSIX에서 지원되지 않으므로 #!/bin/sh스크립트에 사용하면 안됩니다 . 사용하는 read HOST <&10대신. 또한 POSIX는 파일 디스크립터 0-9를 지원하기 위해 쉘만 필요하므로 10<servers.txt스크립트가 엄격하게 준수되는 경우에는 사용할 수 없습니다.
Richard Hansen

28

derobert 설명으로 ssh당신을 읽습니다 stdin.

이 동작을 변경하려면 stdin을 읽지 않도록 -n no ssh를 추가하면됩니다.

ssh -n $HOST "uname -a"

10

아마도 병렬 -ssh 프로젝트 에서 pssh 를 사용하는 것이 더 나을 것입니다 .

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

-i대화식을 의미합니다. pssh에는 병렬 scp 및 병렬 rsync도 제공됩니다. 좋은 점은 비동기식으로 실행되며 요청한만큼 많은 스레드를 실행한다는 것입니다. 디폴트 (-i / interactive가 아님)는 stdout / stderr에 대해 별도의 디렉토리로 출력하는 것이며, 이는 $ outputdir / $ hostname에 의해 수행됩니다.


3

이런 종류의 작업을 자주하는 경우 Fabric 을 사용해보십시오.

지침에 따라 Fabric을 설치하십시오 . 대부분의 경우 필요한 경우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')

그런 다음 run fab mytask은 원하는 결과를 제공합니다.


1

다음과 같은 명령을 사용하는 것이 더 쉽습니다.

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

ssh 명령은 표준 입력에서 모든 스트림을 가져 오기 때문에 while 문에 의해 제공됩니다.

파이프를 사용하여 ssh의 stdin을 다른 소스로 전환 할 수 있습니다.

echo "" | ssh ...

예 :

while read HOST ; do echo "" | ssh $HOST "uname -a" ; done < servers.txt

while 루프에서 모든 ssh 명령의 stdin 입력은 다른 소스로 전환되어야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.