Bashを使用してファイルから行を読み取る:forとwhile

Bashを使用してファイルから行を読み取る:forとwhile

bashスクリプトを使用してテキストファイルを読み取り、各行で特定のタスクを実行しようとしています。

だから、次のリストがあります。

server1
server2
server3
server4

私は次のようにwhileループを使用してこれを繰り返すことができると思いました。

while read server; do
  ssh $server "uname -a"
done < /home/kenny/list_of_servers.txt

whileループは一度実行された後に停止するため、uname -aserver1でのみ実行されます。

しかし、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 …機能しません。頑張れば安全に過ごせます。filecat 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 でファイルを開き、ファイル記述子read3 でコマンドの標準入力をリダイレクトします。 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。から:-nsshman ssh

-n     Redirects stdin from /dev/null (actually, prevents reading from stdin).

答え4

基本的な標準入力処理は、sshwhileループの残りの行をすべて消費します。

この問題を回避するには、そのコマンドが標準入力を読み取る場所を変更します。標準入力をコマンドに渡す必要がない場合は、/dev/null特殊デバイスから標準入力を読み込みます。

関連情報