पाश का उपयोग करते हुए कई सर्वरों के लिए 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
मैट

जवाबों:


98

ssh आपके मानक इनपुट के शेष भाग को पढ़ रहा है।

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

readस्टड से पढ़ता है। <रीडायरेक्ट एक फ़ाइल से 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टर्मिनल से इसकी स्टड को पढ़ना जारी रखता है (या आपकी स्क्रिप्ट कैसे कहलाती है, इस पर निर्भर करता है)।

उपाय

कम से कम बैश में, आप 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, लेकिन निश्चित रूप से अन्य आदेशों में इसकी कमी हो सकती है - अन्य समाधान काम करते हैं, चाहे कोई भी आदेश आपके स्टड को खा रहा हो।

वैकल्पिक समाधान 2: दूसरा पुनर्निर्देशित

आप जाहिरा तौर पर (जैसा कि, मैंने कोशिश की, यह कम से कम बाश के मेरे संस्करण में काम करता है ...) एक दूसरा पुनर्निर्देशन करें, जो कुछ इस तरह दिखता है:

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

आप इसे किसी भी कमांड के साथ उपयोग कर सकते हैं, लेकिन यदि आप वास्तव में टर्मिनल इनपुट कमांड पर जा रहे हैं तो यह मुश्किल होगा।


1
नंबर कहां 10से आता है?
मार्टिन वेगाटर

2
@MartinVegter मैंने इसे बनाया। 0/1/2 स्टडिन, स्टडआउट और स्टडर हैं। आप अपनी पसंद का कोई भी नंबर चुन सकते हैं — बैश आपको बहुत ऊँचा रखने की अनुमति देता है, शायद OS सीमा तक। अन्य गोले आपको कम तक सीमित कर सकते हैं ...
derobert

@MartinVegter मैंने आपकी दोनों टिप्पणियों का जवाब देने के लिए संपादन किया है।
व्युत्पन्न

एक अन्य विकल्प: exec 3<&0; while read HOST; do ssh $HOST "uname -a" <&3; done <servers.txt; exec 3<&- यह फाइल डिस्क्रिप्टर 3 को मूल स्टडिन का एक बैकअप बनाता है, फिर इसे ssh के स्टड के लिए उपयोग करता है, फिर बैकअप एफडी को बंद कर देता है। यह किसी भी POSIX- संगत शेल पर काम करता है।
रिचर्ड हैनसेन

8
-uविकल्प POSIX द्वारा समर्थित है, और इस तरह के लिए नहीं किया जाना चाहिए #!/bin/shलिपियों; read HOST <&10इसके बजाय का उपयोग करें । इसके अलावा, POSIX को केवल 9 के माध्यम से फ़ाइल डिस्क्रिप्टर 0 का समर्थन करने के लिए गोले की आवश्यकता होती है, इसलिए 10<servers.txtयदि स्क्रिप्ट को सख्ती से अनुरूप होना है तो इसका उपयोग नहीं किया जा सकता है।
रिचर्ड हेंसन

28

जैसा कि derobert वर्णन करता sshहै कि आपके पढ़ता है stdin

इस व्यवहार को बदलने के लिए, आप इसे स्टड पढ़ने से रोकने के लिए कोई ssh नहीं जोड़ सकते हैं।

ssh -n $HOST "uname -a"

10

आप शायद समानांतर- एसएचएस परियोजना से pssh का उपयोग करके वास्तव में बेहतर होंगे ।

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

-iइंटरएक्टिव का मतलब है। pssh भी एक समानांतर scp और समानांतर rsync के साथ आता है। क्या अच्छा है कि यह एसिंक्रोनस रूप से चलता है और आप इसे जितना पूछेंगे उतने थ्रेड चलेंगे। डिफ़ॉल्ट (गैर-इंटरैक्टिव / इंटरएक्टिव) stdout / stderr के लिए अलग-अलग निर्देशिकाओं के लिए आउटपुट है, जो यह $ outputdir / $ hostname द्वारा करता है।


3

यदि आप अपने आप को इस तरह के कार्य को अक्सर करते हुए पाते हैं, तो आपको फैब्रिक का प्रयास करना चाहिए

निर्देशों का पालन करते हुए फैब्रिक स्थापित करें , ज्यादातर ऐसे मामले जो आपको चाहिए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आपको मनचाहा परिणाम देगा।


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 commnand मानक इनपुट से सभी स्ट्रीम लेती है, जबकि स्टेटमेंट द्वारा फीड किया जाता है,

आप किसी अन्य स्रोत पर ssh के स्टड को स्विच करने के लिए एक पाइप का उपयोग कर सकते हैं:

echo "" | ssh ...

उदाहरण :

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

लूप में सभी ssh कमांड के स्टड इनपुट को दूसरे स्रोत पर स्विच किया जाना चाहिए।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.