仅当特定单词后未设置行时如何附加行

仅当特定单词后未设置行时如何附加行

我构建了以下 sed cli 以添加以下行

force_https_protocol=PROTOCOL_TLSv1_2

行后:

[security]

sed 命令行界面:

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 Coreutils,逐行连接和反转文件。 U&L 的几个问答中介绍了如何使用其他/标准工具反转文件(例如,命令 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/As 对此进行了大量介绍;例如:如何使用 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)。

相关内容