特定の単語の後に行が設定されていない場合にのみ行を追加する方法

特定の単語の後に行が設定されていない場合にのみ行を追加する方法

次の行を追加するために、次のsed cliをビルドします。

force_https_protocol=PROTOCOL_TLSv1_2

次の行の後:

[security]

sed cli:

sed  -i '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2'  /etc/ambari-agent/conf/ambari-agent.ini

しかし、問題は、その行がforce_https_protocol=PROTOCOL_TLSv1_2 すでに[security]

行がすでに存在する場合に行の追加を無視するように sed 構文を変更するにはどうすればよいでしょうか?

期待される出力

[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

間違ったファイル:

[security]
force_https_protocol=PROTOCOL_TLSv1_2
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

答え1

ファイル内のすべての「force_https_protocol=PROTOCOL_TLSv1_2」が[security]タグ内にあり、他の場所には現れないと仮定します。

sed -e '/^force_https_protocol=PROTOCOL_TLSv1_2$/d'\
    -e '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2' file

アイデアは、ファイル内の「force_https_protocol=PROTOCOL_TLSv1_2」文字列のみを含むすべての行を削除することです。

説明

/^force_https_protocol=PROTOCOL_TLSv1_2$/d'

「force_https_protocol=PROTOCOL_TLSv1_2」文字列を含むすべての行を削除します。

テスト

$ cat file
[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

[security]
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

$ sed -e '/^force_https_protocol=PROTOCOL_TLSv1_2$/d'\
      -e '/\[security\]/a force_https_protocol=PROTOCOL_TLSv1_2' file
[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

[security]
force_https_protocol=PROTOCOL_TLSv1_2
keysdir=/var/lib/ambari-agent/keys
server_crt=ca.crt
passphrase_env_var_name=AMBARI_PASSPHRASE

答え2

1. AWKの使用

より一般的な問題に対する解決策としては、ラベル付きテキストブロック(つまり、ファイル内の特定のラベルに続き、次のラベルまたはファイルの先頭に続くテキスト行)にのみ適用されます。まだ存在しません。

簡単な方法は、ファイルを遡って、最後に遭遇した時刻を追跡することです。特定のラベルに遭遇するたびに(ラベル自体を印刷する直前に)それを印刷しますが、まだそれを見たことがない場合のみです() に遭遇したことを忘れてしまう () ラベルに遭遇するたびに、結果を反転します。そして、もちろん、最後のステップとして結果を反転します。

tac file | awk '
    /^force_https_protocol=PROTOCOL_TLSv1_2$/ {
        seen = 1
    }
    /^\[security\]$/ {
        if ( ! seen ) {
            print "force_https_protocol=PROTOCOL_TLSv1_2"
        }
    }
    /^\[[^]]*\]$/ {
        seen = 0
    }
    1' | tac

tac、含まれるGNU コアユーティリティは、ファイルを行ごとに連結して反転します。他の/標準ツールでファイルを反転する方法は、U&LのいくつかのQ/Aで説明されています(たとえば、コマンド sed '1!G;h;$!d' はどのようにしてファイルの内容を反転するのでしょうか?そして逆グレッピング)。

これは、次のテキスト スニペットのforce_https_protocol=PROTOCOL_TLSv1_2最初の後には挿入されません。[security]

[security]
keysdir=/var/lib/ambari-agent/keys
force_https_protocol=PROTOCOL_TLSv1_2
passphrase_env_var_name=AMBARI_PASSPHRASE

[security]
...

単に挿入するラベルが出現するたびに、すでに存在する場合、同じアプローチで次の結果が生じる可能性があります。

tac file | awk '
    /^force_https_protocol=PROTOCOL_TLSv1_2$/ {
        seen = NR
    }
    /^\[security\]$/ {
        if ( ( NR == 1 ) || ! ( NR == seen + 1 ) ) {
            print "force_https_protocol=PROTOCOL_TLSv1_2"
        }
    }
    1' | tac

(これ意思force_https_protocol=PROTOCOL_TLSv1_2上記のテキストサンプルの後に挿入します[security]

2. sedの使用

sedサイクルのバリエーションを使用できます(N;P;D;これは、U&L の他の Q/A で豊富にカバーされています。たとえば、次のようになります)。sed を使用して複数行の文字列を置き換えるにはどうすればよいですか?またはsed を使用してパターンの後の次の行を編集するにはどうすればよいでしょうか?)。

これにより、前者がまだ存在しない場合にのみ、のみforce_https_protocol=PROTOCOL_TLSv1_2で構成された行の後に行が挿入されます。[security]

sed '
  $! {
    N
    P
    /^\[security\]\n/ {
      /\nforce_https_protocol=PROTOCOL_TLSv1_2$/ b b
      i\
force_https_protocol=PROTOCOL_TLSv1_2
    }
    :b
    D
  }
  s/^\[security\]$/&\
force_https_protocol=PROTOCOL_TLSv1_2/'

このスクリプト:

  • 最後の行 ( ) を除く各行に対して、$!改行とそれに続く次の行をパターン スペース ( N) (現在の行がすでに含まれています) に追加します。
  • パターンスペースの最初の行 ( P) を出力します。その行が[security]... の場合
    • ...次の行はforce_https_protocol=PROTOCOL_TLSv1_2、パターンスペースから最初の行を削除し、新しいサイクル ( b b:b D) を開始します。
    • それ以外の場合は、パターンスペースから最初の行を削除して新しいサイクルを開始する前に、force_https_protocol=PROTOCOL_TLSv1_2標準出力()に印刷します。i\D
  • 最後の行が の場合[security]、 はそれを 自身に続いて改行と に置き換え、force_https_protocol=PROTOCOL_TLSv1_2結果を出力します (暗黙の を使用してp)。

関連情報