Bash - 用第二个数组中的字符串替换第一个数组中的文件字符串

Bash - replace in file strings from first array with strings from second array

我有两个数组。第一个填充了 grep 从我想用下载的新文件替换的文件中编辑的值。

请注意,我不确切知道第一个数组的样子,这意味着某些值将具有 _,其他值为 - 有些人不会有任何这些,并在名称后立即放置 :(冒号)。

示例数组:

array1:

[account:123 shoppingcart-1:123 notification-core_1:123 notification-dispatcher_core_1:123 notification-dispatcher-smschannel_core_1:123]

array2:

[account_custom_2:124 shoppingcart_custom_2:124 notification_custom_2:124 notification-dispatcher_custom_2:124 notification-dispatcher-smschannel_custom_2:124]

这些数组是唯一的例子,有超过 50 个值要替换。

我正在对第二个数组中的每个项目与第一个数组中的每个项目进行比较,如下所示:

file_name="<path_to_file>/file.txt"
for i in "${!array1[@]}"
do
        for j in "${!array2[@]}"
        do
                array_2_base="`echo ${array2[$j]} | awk -F_ '{print }'`"
                if [[ "${array1[$i]}" == *"$array_2_base"* ]]
                then
                        sed -i "s=${array1[$i]}=${array2[$j]}=g" $file_name
                 fi
        done
done

这里我只对第二个数组中的每个项目的第一部分进行子字符串化,这样我就可以将它与第一个数组中的项目进行比较。

例如account_custom_2:124 -> 账户notification-dispatcher_custom_2:124 -> 通知调度程序

这很好用,但是当 notificationnotification-core_1:123[= 时我遇到了问题53=] 和 notification-dispatcher_core_1:123notification-dispatcher-smschannel_core_1:123.

能否请您就如何解决此问题提出建议,或者是否可以建议另一种方法?

我找到了解决这个问题的方法。

替换后我将从第一个数组中删除字符串。

file_name="<path_to_file>/file.txt"
for i in "${!array1[@]}"
do
        for j in "${!array2[@]}"
        do
                array_2_base="`echo ${array2[$j]} | awk -F_ '{print }'`"
                if [[ "${array1[$i]}" == *"$array_2_base"* ]]
                then
                        sed -i "s=${array1[$i]}=${array2[$j]}=g" $file_name
                        delete="${array1[$i]}"
                        array1=( "${array1[@]/$delete}" )
                 fi
        done
done

如果数组中的项数相等,则可以在一个循环中处理它们

for i in "${!array1[@]}"; {
    value=${array1[$i]}
    new_value=${array2[$i]}
    sed -i "s/$value/$new_value/" file
}

重点是array2元素的base可能包含其他元素 作为子字符串,将根据顺序导致不正确的替换 的匹配。 为避免这种情况,您可以 sort 降序排列数组,以便 较长的图案在前。

假设数组中的字符串不包含制表符,将 你请试试:

file_name="<path_to_file>/file.txt"
array1=(account:123 shoppingcart-1:123 notification-core_1:123 notification-dispatcher_core_1:123 notification-dispatcher-smschannel_core_1:123)
array2=(account_custom_2:124 shoppingcart_custom_2:124 notification_custom_2:124 notification-dispatcher_custom_2:124 notification-dispatcher-smschannel_custom_2:124)

# insert the following block to sort array2 in descending order
array2=( $(for j in "${array2[@]}"; do
    array_2_base=${j%%_*}
    printf "%s\t%s\n" "$array_2_base" "$j"
done | sort -r | cut -f2-) )

# the following code will work "as is"
for i in "${!array1[@]}"
do
    for j in "${!array2[@]}"
    do
        array_2_base="`echo ${array2[$j]} | awk -F_ '{print }'`"
        if [[ "${array1[$i]}" == *"$array_2_base"* ]]
        then
            sed -i "s=${array1[$i]}=${array2[$j]}=g" "$file_name"
            delete="${array1[$i]}"
            array1=( "${array1[@]/$delete}" )
        fi
    done
done

由于重复,上面的脚本在执行时间上会很低效 调用 sed -i 命令。
下面的脚本会 运行 更快 通过预先生成 sed 脚本并只执行一次。

file_name="<path_to_file>/file.txt"
array1=(
    account:123
    shoppingcart-1:123
    notification-core_1:123
    notification-dispatcher_core_1:123
    notification-dispatcher-smschannel_core_1:123
)
array2=(
    account_custom_2:124
    shoppingcart_custom_2:124
    notification_custom_2:124
    notification-dispatcher_custom_2:124
    notification-dispatcher-smschannel_custom_2:124
)

while IFS=$'\t' read -r base a2; do     # read the sorted list line by line
    for a1 in "${array1[@]}"; do
        if [[ $a1 == *$base* ]]; then
            scr+="s=$a1=$a2=g;"         # generate sed script by appending the "s" command
            continue 2
        fi
    done
done < <(for j in "${array2[@]}"; do
    array_2_base=${j%%_*}               # substring before the 1st "_"
    printf "%s\t%s\n" "$array_2_base" "$j"
                                        # print base and original element side by side
done | sort -r)

sed -i "$scr" "$file_name"              # execute the replacement at once