替换文件中另一个特定字符串之后出现的匹配字符串

替换文件中另一个特定字符串之后出现的匹配字符串

ssl.cfg我有一个用于 SSL 请求的配置文件 ( ):

oid_section = OIDs

[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn

attributes = v3_req

[OIDs]
OrganizationID = 2.5.4.97        # Don't touch this

[dn]
C = FO
ST = Foobar Monarchy
O = Lorem Ipsum
CN = specific.domain.com
OrganizationID = ABCDE-12345     # Change this
#...

[v3_req]
#...

我正在尝试更换第二 OrganizationID的值 ( ABCDE-12345) 与新值ABCDE-98765.

问题是,大多数匹配都反对OrganizationID两个相同名称之间的冲突,这两个名称必须相同(下面的部分[OIDs]是标记标签;第二次出现实际上是使用标签)。

我尝试过的事情:

1)从此答案改编的代码(匹配后替换行)在堆栈溢出上:

REQ_ID="ABCDE-98765"
sed -i "/CN =/!b;n;c\\OrganizationID = ${REQ_ID}" ssl.cfg
  • 匹配上一行,CN = ...
  • !b中断当前匹配
  • n扫描下一行
  • c使用后面的文本更改选定的行(\打印新行;\\由于双引号而转义 ( ))

我不太喜欢这个解决方案,因为它依赖于上一行的位置。如果有人手动将 CN 交换到不同的线路,这就会中断。

2)利用之前尝试的知识

REQ_ID="ABCDE-98765"
sed -i "/\\\[dn\\\]/!b;/OrganizationID/c\\OrganizationID = ${REQ_ID}" ssl.cfg

在这里,我尝试过:

  • 匹配[dn](双转义[]
  • 打破当前比赛
  • 从该点开始查找下一次出现的 OrganizationID
  • 替换使用c\...

这是行不通的;我有一种感觉,第二场比赛将再次从头开始。我注意到,如果两次出现都匹配,则似乎都没有改变。

我专门寻找一种方法来匹配 OrgID,而不关心它在 [dn] 中的位置。其值中的字符串ABCDE-可能不会改变,但我不想依赖它。对象标识符2.5.4.97 不会改变

总结一下:

  1. 有没有办法从特定行开始浏览文件? (使用[dn]标签)
  2. 是否有其他方法可以实现上述目标? (awk?其他 sysutils + 管道?...)

(我对这个令人困惑的标题表示歉意;我不知道如何更好地表达它)

答案1

$ awk -v org='ABCDE-12345' -v RS= -v ORS='\n\n' '
    $1 == "[dn]" {
        $0 = "\n" $0
        sub(/\nOrganizationID[[:space:]]*=[^\n]*/,"\nOrganizationID = " org)
        sub(/^\n/,"")
    }
    { print }
' file
oid_section = OIDs

[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn

attributes = v3_req

[OIDs]
OrganizationID = 2.5.4.97        # Don't touch this

[dn]
C = FO
ST = Foobar Monarchy
O = Lorem Ipsum
CN = specific.domain.com
OrganizationID = ABCDE-12345
#...

[v3_req]
#...

RS=<null>将 awk 置于空行分隔段落模式(RS = ""参见https://www.gnu.org/software/gawk/manual/gawk.html#Multiple-Line),ORS='\n\n'确保以空行分隔输出,$0 = "\n" $0确保记录的每一行前面都有一个换行符,以便后续sub()使用周围的换行符即使OrganizationID是记录的第一行也可以成功,然后sub(/^\n/,"")删除我们在第一行中添加的临时换行符步。

答案2

以下awk代码会将以 开头的行的值更改为OrganizationID您选择的任何值,但仅限于 部分[dn],并且仅在该键第一次出现时:

awk -F'=' -v OFS='=' '$0=="[dn]"{f=1} (f)&&$1~/^OrganizationID/{$2="Something-New"; f=0} 1' ssl.cfg

如果您想避免对替换进行硬编码,可以将其导入,如下所示

req_id="Something-New"
awk -F'=' -v OFS='=' -v repl="$req_id" '$0=="[dn]"{f=1} (f)&&$1~/^OrganizationID/{$2=repl; f=0} 1' ssl.cfg

(但请注意,变量中的转义序列将由 解释awk,因此如果您需要文字反斜杠,则需要采取特殊的预防措施。请参阅StackOverflow 上的这个答案供进一步阅读)

上面的代码执行以下操作:

  • 它将=输入 ( -F'=') 和输出 ( -v OFS='=') 视为字段分隔符,因此在内部,该行将被分成=“列”,并且输出行将通过连接其间的字段条目来组装=

  • 如果整个当前行 ( $0) 仅包含节标题[dn],则标志f将设置为 1 以指示找到了正确的节。

  • 如果我们位于正确的部分,并且第一个字段($1即第一个字段之前的字符串=)以 开头OrganizationID,则第二个字段($2即第一个字段之后的字符串)将替换为我们设置的变量=的内容的内容awkrepl$req_id通过选项变量-v repl="$req_id"。它还会重置该标志,因此我们不会替换任何其他出现的 的值OrganizationID

  • 1规则块之外的“单独”{ ... }是一种速记符号,表示“打印当前行以及先前规则所做的所有修改”。这个机制背后的想法实际上是你可以陈述任何条件在规则块之外如果它的计算结果为true(或非零),则awk打印当前行(例如,awk 'FNR==1'仅打印第一行,因为内部行计数器仅FNR等于1该行)。

    请注意,如果规则块内没有任何显式print/printf命令,或者评估为 的“全局”条件trueawk不是打印当前行。

您的示例的输出:

oid_section = OIDs

[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn

attributes = v3_req

[OIDs]
OrganizationID = 2.5.4.97        # Don't touch this

[dn]
C = FO
ST = Foobar Monarchy
O = Lorem Ipsum
CN = specific.domain.com
OrganizationID =Something-New
#...

[v3_req]

该代码不会修改文件,而是将结果打印到控制台(因此您可以先检查它是否正确)。一旦您满意,可以将输出重定向到新文件,然后手动替换,或者 - 如果您awk支持 - 使用-i inplace扩展(适用于最近的 GNUawk实现)。

awk -F"=" -v OFS="=" '.. etc ..' ssl.cfg > ssl.cfg.new

或者

awk -i inplace -F"=" -v OFS="=" '.. etc ..' ssl.cfg

答案3

方法1

for  i in `sed -n '/OrganizationID/{;=;}' file| sed -n '2p'`; do sed ''$i's/.*/OrganizationID = ABCDE-98765/g' file; done

Python

#!/usr/bin/python
import re
j=[]
t=0
h=re.compile(r'OrganizationID.*')
k=open('file','r')
for i in k:
    if re.search(h,i):
        t=t+1
        if (t == 2):
            y=re.sub(h,"OrganizationID = ABCDE-98765",i)
            print y.strip()
        else:
            print i.strip()
    else:
        print i.strip()

输出

oid_section = OIDs

[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn

attributes = v3_req

[OIDs]
OrganizationID = 2.5.4.97        # Don't touch this

[dn]
C = FO
ST = Foobar Monarchy
O = Lorem Ipsum
CN = specific.domain.com
OrganizationID = ABCDE-98765

答案4

这是使用 Awk 完成任务的一种方法。我们将 REQ_ID 放置到 awk 命令行的环境中,从这里可以在内置关联数组 ENVIRON 中使用它

$ REQ_ID="ABCDE-98765" \
   awk '
     BEGIN { a[1] = "OrganizationID = " ENVIRON["REQ_ID"] }
     $1 == "[dn]", match($0, /^OrganizationID[[:blank:]]*=/) {
       a[0] = $0
       $0 = a[RSTART]
     }1
  ' ssl.cfg

使用 sed 还需要做一些更多的工作,因为我们必须确保 REQ_ID 可放置在 s/// 命令的 RHS 上。

$ REQ_ID="ABCDE-98765"; s="[[:blank:]]" 
$ req_id_esc=$(printf '%s\n' "$REQ_ID" | sed -e 's:[\&/]:\\&:g')
$ sed -e "/^[[]dn]/,/^OrganizationID$s*=/s/^\(OrganizationID\)$s*=.*/\1 = ${req_id_esc}/" ssl.cfg
$ REQ_ID="ABCDE-98765" \
   perl -pale '
      s//$1 = $ENV{REQ_ID}/ if 
        $F[0] eq "[dn]" ... /^(OrganizationID)\s*=.*/ and
        defined $1' ssl.cfg

相关内容