Bash - заменить в файле строки из первого массива на строки из второго массива - PullRequest
0 голосов
/ 17 февраля 2020

У меня есть два массива. Первый заполнен значениями grep ed из файла, который я хочу заменить новыми загруженными.

Обратите внимание, что я точно не знаю, как будет выглядеть первый массив, то есть некоторые значения будут иметь _ , другие - , а некоторые не будут иметь ничего из этого, и сразу после имени будет помещено : (двоеточие).

Пример массивов:

массив1:

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

массив2:

[account_custom_2: 124 shoppingcart_custom_2: 124 messages_custom_2: 124 messages-dispatcher_custom_2: 124 messages-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 $1}'`"
                if [[ "${array1[$i]}" == *"$array_2_base"* ]]
                then
                        sed -i "s=${array1[$i]}=${array2[$j]}=g" $file_name
                 fi
        done
done

Ей e Я подстроку только первой части для каждого элемента во втором массиве, чтобы я мог сравнить его с элементом в первом массиве.

например account_custom_2: 124 -> account или messages-dispatcher_custom_2: 124 -> диспетчер уведомлений .

Это работает хорошо, но я сталкиваюсь с проблемой, когда уведомление находится в Notification-Core_1: 123 и Notification-dispatcher_core_1: 123 и Notification-dispatcher-smschannel_core_1: 123 .

Можете ли вы дать совет, как это исправить или вы можете предложить другой подход к этому?

Ответы [ 3 ]

1 голос
/ 17 февраля 2020

Если количество элементов в ваших массивах равно, вы можете обработать их за один l oop

for i in "${!array1[@]}"; {
    value=${array1[$i]}
    new_value=${array2[$i]}
    sed -i "s/$value/$new_value/" file
}
1 голос
/ 18 февраля 2020

Дело в том, что base элемента array2 может включать другой элемент в качестве подстроки и приведет к неправильной замене в зависимости от порядка сопоставления. Чтобы избежать этого, вы можете 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 $1}'`"
        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
0 голосов
/ 17 февраля 2020

Я нашел способ исправить это.

Я удаляю строку из первого массива после замены.

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 $1}'`"
                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
...