ここでfor
ループは正常です。ただし、ファイルにはマシン名が含まれており、マシン名には空白文字やグロビング文字が含まれていないためです。一般に、シェルはコマンドの出力を空白がある場所で分割し、次に各単語をグロブパターンとして扱い、さらに展開されるため、一般的にfor x in $(cat file); do …
行を反復処理しません。あなたがそれに取り組むならば、あなたは安全にすることができます:file
cat file
\[?*
for x in $(cat file)
set -f
IFS='
'
for x in $(cat file); do …
関連資料:名前にスペースが含まれるファイルをループしますか?; bashの変数から行ごとに読み取るにはどうすればよいですか?; なぜではwhile IFS= read
なく、頻繁に使用されるのIFS=; while read..
ですか?を使用する場合while read
、行を読み取るための安全な構文はwhile IFS= read -r line; do …
です。
それでは、あなたのwhile read
試みで何がうまくいかないかを見てみましょう。サーバーリストファイルからのリダイレクトは、ループ全体に適用されます。そのため、ssh
実行時の標準入力はそのファイルから取得されます。sshクライアントは、リモートアプリケーションが標準入力からの読み取りをいつ必要とするかを知ることができません。そのため、sshクライアントは何らかの入力を認識するとすぐに、その入力をリモート側に送信します。そこにあるsshサーバーは、必要に応じて、その入力をリモートコマンドに送る準備ができています。あなたの場合、リモートコマンドは入力をまったく読み取らないため、データは破棄されますが、クライアント側はそれについて何も知りません。あなたの試みは、入力をまったく読み取らず、標準入力をそのままにしecho
ているため、うまくecho
いきました。
これを回避する方法はいくつかあります。-n
オプションを使用して、sshに標準入力から読み取らないように指示できます。
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ループ全体で行う必要があります。
一般的な解決策は、標準入力以外のファイル記述子でループに入力を提供することです。シェルには、1つの記述子番号から別の記述子番号への入出力をフェリーする構造があります。ここでは、ファイル記述子3でファイルを開き、ファイル記述子3 read
からコマンドの標準入力をリダイレクトします。sshクライアントは開いている非標準記述子を無視するため、すべて正常です。
while read server <&3; do
ssh $server "uname -a"
done 3</home/kenny/list_of_servers.txt
bashでは、read
コマンドには異なるファイル記述子から読み取るための特定のオプションがあるため、を書き込むことができますread -u3 server
。
関連読書:ファイルディスクリプタ&シェルスクリプト。いつ追加のファイル記述子を使用しますか?