テキストファイルを読み取って、bashスクリプトを使用して、各行で何かを実行しようとしています。
だから、私はこのようなリストを持っています:
server1
server2
server3
server4
次のように、whileループを使用してこれをループできると思いました。
while read server; do
ssh $server "uname -a"
done < /home/kenny/list_of_servers.txt
Whileループは1回の実行後に停止するため、uname -a
on server1
ただし、猫を使用した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
最初の例が最初の反復の後に停止するのはなぜですか?
for
ループはここでは問題ありません。ただし、これは、ファイルに空白文字やグロビング文字が含まれていないマシン名が含まれているためです。 for x in $(cat file); do …
は、一般にfile
の行を反復処理するようには機能しません。これは、シェルが最初にコマンド_cat file
_からの出力を空白がある場所で分割し、次に各Wordを処理するためです。グロブパターンなので、_\[?*
_はさらに拡張されます。作業する場合は、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
_の試行で問題が発生した問題に移ります。サーバーリストファイルからのリダイレクトは、ループ全体に適用されます。したがって、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回開かれます。
一般的な解決策は、標準入力以外の ファイル記述子 でループに入力を提供することです。シェルには、1つの記述子番号から別の記述子番号への入力と出力をフェリーする構成があります。ここでは、ファイル記述子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
_を書き込むことができます。
関連資料: ファイル記述子とシェルスクリプト ; いつ追加のファイル記述子を使用しますか?
while
ではなく---(for
を使用する必要があります。そのようなループでコマンドが標準入力を飲み込むのを避ける方法は、単純に別の ファイル記述子 を使用することです。
while read -u 9 server; do
ssh $server "uname -a"
done 9< /home/kenny/list_of_servers.txt
詳細については、 help [r]ead
( 本当に )および 理由を説明する別の記事 。
最初のコードでは、ssh
はwhile
からSTDINを「盗み」ます。追加 -n
オプションをssh
に追加して回避します。 man ssh
:
-n Redirects stdin from /dev/null (actually, prevents reading from stdin).
ssh
のデフォルトの標準入力処理は、whileループから残りの行を排出します。
この問題を回避するには、問題のあるコマンドが標準入力を読み取る場所を変更します。コマンドに標準入力を渡す必要がない場合は、特別な/dev/null
デバイスから標準入力を読み取ります。