不必要に複数の重複行 -sed
postgresqlデータベースからアドレスを選択し、SSHを介して選択したアドレスに接続し、各ホスト設定の正しい場所に特定の行を追加することがあります。
たとえば、中間に値を含むファイルがあり、異なる値を持つ新しい行を挿入する必要があります。
# grep hostname config.cfg
hostname, port = 2234: Vasya
hostname, ip address: John, 192.168.1.5
hostname: Andrew
たとえば、次の行
hostname: Andrew
新しい行に新しい値を挿入する必要があります。
hostname: Sheglina
私はこれを行います(最初に1つのホストを試してみます):
for HOST in 192.168.1.1; do ssh $HOST "for VALUE in $(nl -ba /root/config.cfg|grep hostname | tail -1 | awk '{print $1}'); do sed -i $VALUE'a\hostname: Sheglina'/root/config.cfg; done"; done
このコマンドは仮想マシン用です。
# grep hostname config.cfg
hostname, port = 2234: Vasya
hostname, ip address: John, 192.168.1.5
hostname: Andrew
hostname: Sheglina
ただし、物理サーバーでフィルタを介してIPアドレスを選択した後、次のコマンドを適用しようとします。
for HOST in $(psql -U postgres name_db -c "SELECT name, ip_address FROM servers ORDER BY ip_address;" | grep -i 'moscow' | awk '{print $5}'); do ssh $HOST "for VALUE in $(nl -ba /root/config.cfg|grep hostname | tail -1 | awk '{print $1}'); do sed -i $VALUE'a\hostname: Sheglina '/root/config.cfg;done"; done
その後、各行でホスト名:Sheglina行が繰り返されます。/root/config.cfg、一箇所にはありません。必要ありません。このコマンドが仮想マシンでは機能しますが、サーバーでは機能しない理由は理解できません。クラスタ内の別々のノードに行き、同じノードに対してコマンドを実行しても重複した行が出てきますが、なぜですか?
実サーバーでは、次のようになります。
# grep hostname config.cfg
hostname: Sheglina
hostname: Sheglina
hostname: Sheglina
hostname: Sheglina
...
ホスト名の値を持つ以前の行はファイルに残りますが、何らかの理由ですべての行でSeglina行が繰り返されます。
答え1
必要以上に仕事を複雑にしています。長くて複雑で読みにくい1行ですべてを行うのではなく、段階的に分けて順番に実行してください。
このスクリプトは2つのことを行う必要があります。
- 名前フィールドがpsqlに似ているIPアドレスのリストを取得する
moscow
(大文字と小文字を区別する) - 付属の行の直後にを使用して各行を接続し
ssh
て挿入します。hostname: Sheglina
/root/config.cfg
hostname: andrew
たとえば、
#!/bin/bash
city='moscow'
sql="SELECT DISTINCT ip_address FROM servers WHERE name ILIKE '$city'"
# get the query output into an array called 'hosts'
hosts=( $(psql -U postgres name_db -t --csv -c "$sql") )
for host in "${hosts[@]}"; do
ssh "$host" "sed -i -e 's/^hostname: andrew/&\nhostname: Sheglina/i' /root/config.cfg"
done
これは、psqlの組み込みオプションを使用して、必要なデータを正しい形式に抽出します。 -t
ヘッダーとフッターの出力を抑制し、--csv
コンマ区切り形式を要求します。 SQLクエリは一致する値のみを選択します。これをgrepまたはawkにパイプする必要はなく、クエリに「DISTINCT」を使用して重複を防ぎます。
次に、ssh
各ホストに接続するために使用され、sedはその後に検索して追加するためにhostname: andrew
使用されます。hostname: Sheglina
注:シェルでは正しい引用符が難しい場合があります。特に、引用符が必要な他のプログラム(psqlやsshなど)に引用符を渡す必要がある場合は、そうです。printf '%q'
文字列をそのコマンドで使用できる適切に引用された形式に変換する形式を使用できますが、引用の前にposixに似たスタイルを使用するように指示しない限り、postgresは独自の引用/エスケープスタイルを使用しますE
。これは、引用符付き文字列にposixスタイルの文字列エスケープを含めることができることをpostgresに伝えます。
つまり、E'quoted-string'
ちょうど'quoted-string'
。
たとえば、$ cityにシェルとpostgresでエスケープする必要がある文字が含まれている場合は、次のように使用します。
sql="SELECT DISTINCT ip_address FROM servers WHERE name ILIKE E'%"$(printf "%q" "$city")"'"