bashスクリプトを使用してテキストファイルを読み取り、各行で特定のタスクを実行しようとしています。
だから、次のリストがあります。
server1
server2
server3
server4
私は次のようにwhileループを使用してこれを繰り返すことができると思いました。
while read server; do
ssh $server "uname -a"
done < /home/kenny/list_of_servers.txt
whileループは一度実行された後に停止するため、uname -a
server1でのみ実行されます。
しかし、catを使用するforループはうまく機能します。
for server in $(cat /home/kenny/list_of_servers.txt) ; do
ssh $server "uname -a"
done
私をさらに混乱させることは、これも機能するということです:
while read server; do
echo $server
done < /home/kenny/list_of_servers.txt
最初の繰り返しの後に最初の例が停止するのはなぜですか?
答え1
ここの循環はfor
素晴らしいです。ただし、ファイルには空白文字やワイルドカード文字が含まれていないコンピュータ名が含まれているためです。一般に、行を繰り返すことは、シェルが最初に空白のあるコマンドの出力を分割してから、さらに拡張するために各単語をglobパターンとして扱うため、for x in $(cat file); do …
機能しません。頑張れば安全に過ごせます。file
cat file
\[?*
for x in $(cat file)
set -f
IFS='
'
for x in $(cat file); do …
関連読書:名前にスペースを含むファイルを繰り返しますか?;Bashの変数から1行ずつ読み込むには?;while IFS= read
代わりに、なぜそれほど頻繁に使用されますかIFS=; while read..
?while read
行を読む安全な構文はですwhile IFS= read -r line; do …
。
それでは、あなたの試みwhile read
で何が間違っているのか見てみましょう。サーバーリストファイルのリダイレクトはループ全体に適用されます。したがって、runを実行すると、ssh
標準入力がこのファイルから出力されます。 SSH クライアントは、リモートアプリケーションがいつ標準入力からデータを読み取ろうとするかを知ることができません。したがって、SSH クライアントが一部の入力を検出すると、その入力をリモート側に送信します。その後、SSHサーバーは必要に応じてその入力をリモートコマンドに提供できます。あなたの場合、リモートコマンドは入力をまったく読みませんので、データは破棄されますが、クライアントはこれについて何も知りません。あなたの試みは、入力が読み取られず、標準入力のみを維持するためecho
に機能します。echo
これを防ぐ方法はいくつかあります。オプションを使用して、sshが標準入力を読み取らないように指示できます-n
。
while read server; do
ssh -n $server "uname -a"
done < /home/kenny/list_of_servers.txt
この-n
オプションは、実際にssh
入力をリダイレクトするように指示します。/dev/null
。これはシェルレベルで実行でき、すべてのコマンドで機能します。
while read server; do
ssh $server "uname -a" </dev/null
done < /home/kenny/list_of_servers.txt
ファイルからssh入力が入らないようにする最善の方法は、read
次のコマンドにリダイレクトを適用することですwhile read server </home/kenny/list_of_servers.txt; do …
。コマンドが実行されるたびにファイルが再び開かれるため、機能しませんread
(したがって、ファイルの最初の行を繰り返し読みます)。ループ中にファイルが一度開くように、リダイレクトはwhileループ全体で行われる必要があります。
一般的な解決策は、ループに入力を提供することです。ファイル記述子標準入力は除く。シェルには、ある記述子番号から別の記述子番号に入出力を転送する構造があります。ここでは、ファイル記述子 3 でファイルを開き、ファイル記述子read
3 でコマンドの標準入力をリダイレクトします。 SSHクライアントは公開された非標準記述子を無視するので、すべてがうまく機能します。
while read server <&3; do
ssh $server "uname -a"
done 3</home/kenny/list_of_servers.txt
Bashのread
コマンドには、他のファイル記述子から読み取ることができる特定のオプションがあるためread -u3 server
。
答え2
使用する必要がありますwhile
、いいえfor
。そのようなループで標準入力を飲み込むコマンドを避ける方法は、単に別のコマンドを使用することです。ファイル記述子:
while read -u 9 server; do
ssh $server "uname -a"
done 9< /home/kenny/list_of_servers.txt
より多くの情報を知りたい場合は、help [r]ead
(本物)と他の記事ではその理由を説明します。。
答え3
これを防ぐには、最初のコードにssh
オプションを追加してくださいwhile
。から:-n
ssh
man ssh
-n Redirects stdin from /dev/null (actually, prevents reading from stdin).
答え4
基本的な標準入力処理は、ssh
whileループの残りの行をすべて消費します。
この問題を回避するには、そのコマンドが標準入力を読み取る場所を変更します。標準入力をコマンドに渡す必要がない場合は、/dev/null
特殊デバイスから標準入力を読み込みます。