次のことをしたい
- パターン()を使用して
^[ \t]*services:[ \t]*
ファイルの内容を検索する - 見つかったら、パターンを文字列に置き換えます(変数内)。
- それ以外の場合は、文字列(変数)をファイルの末尾に追加します。
ステップ2で停止しました。スクリプトのステップ2はservices:
ファイル内で置き換えられます${serviceLoopBack}
。私のコマンドには何の問題がありますかsed
?
テストスクリプト
#!/bin/bash
set -e
destfile=config.yml
serviceLoopBack="services:|NL|- name: loopback-service|NL||FS||FS|url: http://127.0.0.1:12345|NL||FS||FS|routes:|NL||FS||FS|- name: loopback-test-route"
serviceLoopBackWithFrontSpace="|NL||NL|$serviceLoopBack"
if grep -E "^[ \t]*services:[ \t]*" $destfile
then
# service tag found, replace target string
sed -i -e 's/^[ \t]*services:[ \t]*/${serviceLoopBack}/g' $destfile
else
# service tag not found, append at end of file
echo $serviceLoopBackWithFrontSpace >> $destfile
fi
# replace separator 1 to new line
sed -i -e 's/|NL|/\n/g' $destfile
# replace separator 2 to space
sed -i -e 's/|FS|/ /g' $destfile
答え1
問題は、2番目のsed
呼び出しでプログラムを一重引用符()で囲むことです' ... '
。これは推奨される方法ですが、一重引用符を使用すると、シェルがシェル変数(たとえば)を拡張するのを防ぐことができるため、テキスト${serviceLoopBack}
はシェル変数の内容に置き換えられずに残ります。
1つの可能性は、次のように単一引用符の代わりに二重引用符を使用することです。
sed -i -e "s,^[ \t]*services:[ \t]*,${serviceLoopBack}," "$destfile"
または、sed
次のコマンドを使用して式をシェル変数にアセンブルしますprintf
。
printf -v prog 's,^[ \t]*services:[ \t]*,%s,' "$serviceLoopBack"
sed -i -e "$prog" "$destfile"
ノート代替テキストも含まれているため、,
区切り文字として使用しています。それ以外の場合は混乱する可能性があります。また、行ごとに一致が1つしかないと仮定しているため、そのオプションは省略しました。/
/
sed
g
とにかくシェルスクリプトの参照を見たいかもしれません。たとえば、この回答理由について。
ただし、sed
厳密に要求されない場合は、ほぼ完全なシェルスクリプトをプログラムに置き換えることができますawk
(より高速)。
awk -v slb="services:|NL|- name: loopback-service|NL||FS||FS|url: http://127.0.0.1:12345|NL||FS||FS|routes:|NL||FS||FS|- name: loopback-test-route" \
'/^[ \t]*services:/{$0=slb;f=1}
{gsub(/\|NL\|/,"\n"); gsub(/\|FS\|/," ")} 1; END{if (!f) {print "\n\n" slb}}' "$destfile"
awk
これにより、変数に「サービスループバック」仕様がインポートされますslb
。- 次に、パターンに一致する行があるかどうかを確認し
services:
、その場合は行全体を保存された文字列に置き換えますslb
(私の考えでは、これが実際に達成したい文字列です)。同時にフラグをf
1に設定します。 - すべての改行文字とすべての空白の発生
gsub()
を置き換えるには、を使用します。|NL|
|FS|
- 一見すると、「汚れた」コマンドは、これまでに変更されたすべての内容を含む現在の行を印刷するように指示
1
します。awk
- 最後に
f
まだ設定されていない場合は、先行パターンを「サービスループバック」行に追加します(ただし、に変換されます\n\n
)。
awk
このオプションを理解する実装がないと、ファイルは内部で編集されません。出力を一時ファイルにリダイレクトし、それを一時ファイルに置き換える必要があります。awk
-i inplace
$destfile
ただし、要約すると、構造化言語(YAML)で作業しているので、専用パーサーを使用することをお勧めしyq
ます(行ベースのテキスト処理ツールの代わりに)。
答え2
コマンドの問題は、sed
変数が一重引用符で拡張されないことです。したがって、sedスクリプトを二重引用符で囲む必要があります。表示するには:
$ var="foo"
$ echo bar | sed 's/bar/$var/'
$var
$ echo bar | sed "s/bar/$var/"
foo
次の問題は、代替文字列に含まれているため、/
別の区切り文字を使用する必要があることです。それは次のとおりです。
sed -i -e "s#^[ \t]*services:[ \t]*#${serviceLoopBack}#g" "$destfile"
ポイントは何ですかg
? 1行につき複数の一致を期待していますか?
以下は、いくつかの改善点を含む作業バージョンのスクリプトです(引用符、grep -qm1
出力の削除、最初の一致で停止)。
#!/bin/bash
set -e
destfile=config.yml
serviceLoopBack="services:|NL|- name: loopback-service|NL||FS||FS|url: http://127.0.0.1:12345|NL||FS||FS|routes:|NL||FS||FS|- name: loopback-test-route"
serviceLoopBackWithFrontSpace="|NL||NL|$serviceLoopBack"
if grep -Eqm 1 "^[ \t]*services:[ \t]*" "$destfile"
then
# service tag found, replace target string
sed -i -e "s#^[ \t]*services:[ \t]*#${serviceLoopBack}#g" "$destfile"
else
# service tag not found, append at end of file
printf '%s\n' "$serviceLoopBackWithFrontSpace" >> "$destfile"
fi
# replace separator 1 with newline and separator 2 with space
sed -i -e 's/|NL|/\n/g' -e 's/|FS|/ /g' "$destfile"