複数の異なるサーバーに接続されたpostgresqlデータベースCLIの複数のインスタンスにコマンドをブロードキャストするために使用するこのようなスクリプトがあります。ハードコーディングされたプロセス交換セットを使用しています。
#!/bin/bash
# names have been changed to protect the guilty
cred="user=dbadmin password=SECRET"
domain=example.com
tee \
>( psql -X "host=db1.$domain dbname=db1 $cred" ) \
>( psql -X "host=db2.$domain dbname=db2 $cred" ) \
>( psql -X "host=db3.$domain dbname=db3 $cred" ) \
>( psql -X "host=xdb1.$domain dbname=xdb1 $cred" ) \
> /dev/null
wait
私がしたいのは、forループを使用して代替配列を作成し、次のようにその配列をteeに渡すことです。
tee "${p[@]}" > /dev/null
しかし、ループを使用すると、teeは各項目にエラーを提供するため、$p
各項目を取得します。/dev/fd/63
tee: /dev/fd/63: No such file or directory
動作しないコードの例:
p=()
for z in db1 db2 db3 xdb1
do
p+=( >( psql -X "host=$z.$domain dbname=$z $cred" ) )
done
tee "${p[@]}" > /dev/null
これを行う方法はありますか?
答え1
これは次の行で発生します。
p+=( >( psql -X "host=$z.$domain dbname=$z $cred" ) )
...bashはこの行を完全なコマンドと見なします。プロセスの交換が発生すると、コマンドが完了すると交換されたプロセスのSTDINが閉じます。
私の考えでは、これを行う方法は2つしかありません。
eval
。そこに行かないでください。exec
。そこに行こう:
p=()
for z in db1 db2 db3 xdb1
do
exec {fd}> >(psql -X "host=$z.$domain dbname=$z $cred")
p+=( $fd )
done
cd /dev/fd && exec tee "${p[@]}" >/dev/null
この{fd}>
構文を使用すると、bashは新しいファイル記述子を割り当て、その値を割り当てて$fd
からそれをプッシュします$p
。
これで作成する$p
必要があるファイルディスクリプタ番号がたくさんあるので、ファイルディスクリプタ番号を実際のファイルに書き込んでから呼び出します。tee
cd
/dev/fd
tee
(猫が皮をむく方法はいくつかありますが、これが最初に浮かぶ最も簡単な方法です)
答え2
以下は、私の意見で私が意味するものを示す簡単な例です。
forループ(各psqlコマンドは成功するまで繰り返され、バックグラウンドサブシェルで実行されます)。生成されたプロセスはファイル記述子の配列を置き換える必要はありません。
cred='user=dbadmin password=SECRET'
domain='example.com'
tf=$(mktemp)
cat > "$tf"
for z in db1 db2 db3 xdb1 ; do
( while ! psql -X "host=$z.$domain dbname=$z $cred" < "$tf" ; do : ; done) &
done > /dev/null
# don't delete the tempfile until all jobs have completed
wait
rm -f "$tf"
各再試行の間にわずかな遅延を与えるには、ループの代わりにsleep x
オプションで使用します。:
while
よりスマートなバージョンでは、whileループ内でより多くの操作を実行し、シャットダウンステータスおよび/またはgrep stderrをチェックしてエラーの原因を見つけ、適切に対応します。