Sed 动态搜索带和不带“+”字符的字符串

Sed dynamically search for string with and without '+' character

所以我有一个被各种脚本解析的配置文件,所以格式是不能改的,不过只要严格遵守格式,内容是可以的。此文件默认包含

等字符串
multiconfig:nmb-devel:nmb-trs-devel
multiconfig:nmb-deploy:nmb-trs-deploy
multiconfig:nmb-deploy:nmb-trs-deploy+

multiconfig:ijk-devel:ijk-trs-devel
multiconfig:ijk-deploy:ijk-trs-deploy
multiconfig:ijk-deploy:ijk-trs-deploy+

multiconfig:qrs-devel:qrs-trs-devel
multiconfig:qrs-deploy:qrs-trs-deploy
multiconfig:qrs-deploy:qrs-trs-deploy+

目前,我有一个脚本可以将这些配置(multiconfig:...)解析成一个数组,以及一个配置数组来替换这些原来的配置。 例如

忽略后面的尾部操作,ACTUAL conf.txt 包含另一个要跳过的匹配项,这就是成功完成的。

TARGETS="multiconfig:new-devel:new-trs-devel multiconfig:newer-devel:newer-trs-devel multiconfig:newest-devel:newest-trs-devel"
NEW_TARGETS_ARR=( $TARGETS )
OLD_TARGETS_ARR=($(sed -n '/multiconfig/p' conf.txt | tail -n +2 | awk '!seen[[=11=]]++'))

注意: 不是有问题的sed操作

这些工作正常,并产生适当的数组,例如:

NEW_TARGETS_ARR: multiconfig:new-devel:new-trs-devel, multiconfig:newer-devel:newer-trs-devel, multiconfig:newest-devel:newest -trs-开发

OLD_TARGETS_ARR: multiconfig:nmb-devel:nmb-trs-devel, multiconfig:nmb-deploy:nmb-trs-deploy, multiconfig:nmb-deploy:nmb-trs -deploy+, multiconfig:ijk-devel:ijk-trs-devel, multiconfig:ijk-deploy:ijk-trs-deploy, multiconfig:ijk-deploy:ijk-trs-deploy+, multiconfig:qrs -devel:qrs-trs-devel, multiconfig:qrs-deploy:qrs-trs-deploy, multiconfig:qrs-deploy:qrs-trs-deploy+

我的objective是用新配置替换旧配置

我现在执行此操作的方法是遍历 OLD_TARGETS_ARR 并将每个 OLD_TARGETS_ARR[index] 替换为 NEW_TARGETS_ARR[index]:

for i in ${!OLD_TARGETS_ARR[@]}
do
        if [ "${NEW_TARGETS_ARR[$i]}" = "" ]; then
                NEW_TARGETS_ARR[$i]="[EMPTY]"
        fi


        echo $i
        echo "OLD TARGET $i: ${OLD_TARGETS_ARR[$i]}"
        echo "NEW TARGET $i: ${NEW_TARGETS_ARR[$i]}"
        echo "sed -i \"s/${OLD_TARGETS_ARR[$i]}/${NEW_TARGETS_ARR[$i]}/g\" conf.txt"
        sed -i "s/${OLD_TARGETS_ARR[$i]}/${NEW_TARGETS_ARR[$i]}/g" conf.txt
done

现在,理论上这应该(或我想要的)结果如下

conf.txt

multiconfig:new-devel:new-trs-devel
multiconfig:newer-devel:newer-trs-devel
multiconfig:newest-devel:newest-trs-devel

[EMPTY]
[EMPTY]
[EMPTY]

[EMPTY]
[EMPTY]
[EMPTY]

虽然实际结果如下

conf.txt

multiconfig:new-devel:new-trs-devel
[EMPTY]
[EMPTY]+

[EMPTY]
[EMPTY]
[EMPTY]+

[EMPTY]
[EMPTY]
[EMPTY]+

虽然我还没有弄清楚为什么多于必要的条目被替换为“[EMPTY]”,但我发现 'deploy+' 上的“+”没有被解析,并且必须以某种方式、形状或形式转义。鉴于这些 sed 操作是动态的,我不能简单地在“+”前面添加一个 \。首先,如何确保“+”被解析为要在 sed 操作中搜索的字符串的一部分?

其次,我可能误解了 sed 的一些基本部分,或者我的循环,尽管为什么除了第一个匹配之外的所有内容都被替换为“[EMPTY]”?

感谢任何人提供的任何意见,

先谢谢大家了!

假设您正在尝试匹配 nmb,使用您的 NEW_TARGETS 数组,您可以尝试这个实现

#!/usr/bin/env bash

NEW_TARGETS=(multiconfig:new-devel:new-trs-devel multiconfig:newer-devel:newer-trs-devel multiconfig:newest-devel:newest-trs-devel)

i=0
while read -r line; do 
    sed "/^multiconfig:nmb/s/.*/${NEW_TARGETS[$i]}/;/new\|^$/! c\[EMPTY]" <<< $line
    i=$((i+1))
done < input_file

输出

multiconfig:new-devel:new-trs-devel
multiconfig:newer-devel:newer-trs-devel
multiconfig:newest-devel:newest-trs-devel

[EMPTY]
[EMPTY]
[EMPTY]

[EMPTY]
[EMPTY]
[EMPTY]

使用 sed 替换字符串文字的主要问题是您需要对这些字符串进行转义,以使它们在此上下文中不具有任何特殊含义(例如,参见 Escape a string for a sed replace pattern)。

另一个特定于您的代码的问题是您在 shell 循环中调用 sed。这非常慢,应尽可能避免。您可以通过构建一个包含所有 sub-commands 的 sed 命令来解决这个问题(例如 sed 's/old1/new1/;s/old2/new2/;...') 但那是 sub-optimal 因为每一行都将由每个 sub-command ,并且也容易出错(例如,当 new1 是以下 sub-command 中替换的目标时)。

考虑到所有这些,sed 似乎并不是完成这项工作的最佳工具,尤其是当您有其他标准工具(如 awk 可以高效地完成此工作时:

#!/bin/bash

NEW_TARGETS_ARR=(
    multiconfig:new-devel:new-trs-devel
    multiconfig:newer-devel:newer-trs-devel
    multiconfig:newest-devel:newest-trs-devel
)
OLD_TARGETS_ARR=(
    $(
        sed -n '/multiconfig/p' conf.txt |
        tail -n +2 |
        awk '!seen[[=10=]]++'
    )
)
for (( i = ${#NEW_TARGETS_ARR[@]}; i < ${#OLD_TARGETS_ARR[@]}; i++))
do
     NEW_TARGETS_ARR[i]='[EMPTY]'
done

awk -v from="${OLD_TARGETS_ARR[*]}" -v to="${NEW_TARGETS_ARR[*]}" '
    BEGIN{
        fromCount = split(from,fromArr)
        toCount = split(to,toArr)
        for (i=1; i<=fromCount; i++)
            tr[fromArr[i]] = toArr[i]
    }
     in tr {  = tr[] }
    1
' conf.txt
multiconfig:new-devel:new-trs-devel
multiconfig:newer-devel:newer-trs-devel
multiconfig:newest-devel:newest-trs-devel

[EMPTY]
[EMPTY]
[EMPTY]

[EMPTY]
[EMPTY]
[EMPTY]

解释

填充 OLD_TARGETS_ARRNEW_TARGETS_ARR 后,我将它们作为参数传递给 awk(作为 space 分隔字符串):

  • BEGIN { ... } 块中,我将 re-split 字符串参数转换为数组并构建一个关联数组,用于将 old 值转换为它的值。

  • in tr { = tr[] } 表示如果当前行的第一列是替换目标,那么我就进行替换。

  • 1 打印当前行。

这可能对你有用(GNU sed & bash):

sed -E '1{x;s/.*/echo '"${NEW_TARGETS_ARR[*]}"'/e;x}
        /multiconfig/!b;g;//!{s/.*/[EMPTY]/p;d};s/ .*//;x;s/\S+ ?//;x' file

将新目标放入 sed hold space(space 分开)。

仅关注包含 multiconfig.

的行

用新目标替换当前行。

如果当前行为空,则替换为[EMPTY],打印后删除。

否则,移除除第一个目标以外的所有目标,切换到保留 space 并通过移除第一个目标及其后续 space 为下一个目标做准备,然后切换回当前行并打印出来。