Sed динамически ищет строку с символом «+» и без него

Итак, у меня есть файл конфигурации, который анализируется различными сценариями, поэтому формат нельзя изменить, хотя содержимое можно, если формат строго соблюдается. Этот файл по умолчанию содержит такие строки, как

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[$0]++'))

Примечание:Не рассматриваемая операция sed

Они отлично работают и приводят к правильным массивам, таким как:

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

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-развертывание: qrs-trs-развертывание +

Моя цель — заменить старые конфигурации новыми.

Мой способ сделать это прямо сейчас — пройтись по 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]+

Хотя я не понял, почему больше записей, чем необходимо, заменяется на «[ПУСТОЙ]», я понял, что на «+» на «развернуть +» не анализируется и должен быть каким-то образом экранирован, форма, или форма. Учитывая, что эти операции sed являются динамическими, я не могу просто добавить \ перед «+». Во-первых, как я могу убедиться, что «+» анализируется как часть строки для поиска в операции sed?

Во-вторых, я, вероятно, неправильно понимаю какую-то основную часть sed или мой цикл, хотя почему все, кроме первого совпадения, заменяется на «[EMPTY]»?

Я ценю любой вклад, который кто-либо может дать,

Спасибо всем заранее!

Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
2
0
99
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Предполагая, что вы пытаетесь сопоставить 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 для замены строковых литералов заключается в том, что вам нужно экранировать эти строки, чтобы они не имели особого значения в этом контексте (см., например, Экранирование строки для шаблона замены sed).

Другая проблема, специфичная для вашего кода, заключается в том, что вы вызываете sed внутри цикла оболочки. Это очень медленно, и его следует избегать, когда это возможно. Вы можете обойти это, создав одну команду sed, содержащую все подкоманды (например, sed 's/old1/new1/;s/old2/new2/;...'), но это неоптимально, потому что каждая строка будет проверена каждой подкомандой, а также подвержена ошибкам (например, когда new1 является целью замену в следующей подкоманде).

Имея все это в виду, 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[$0]++'
    )
)
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]
    }
    $1 in tr { $1 = tr[$1] }
    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_ARR и NEW_TARGETS_ARR заполнены, я передаю их в качестве аргументов awk (в виде строк, разделенных пробелами):

  • В блоке BEGIN { ... } я повторно разбиваю строковые аргументы на массивы и создаю ассоциативный массив для преобразования значения старый в его значение новый.

  • $1 in tr { $1 = tr[$1] } означает, что если первый столбец текущей строки является целью замены, то я выполняю замену.

  • 1 печатает текущую строку.

На самом деле это очень близко к моему желаемому решению, хотя, поскольку этот вопрос относится к методу sed, не могли бы вы объяснить логику? Спасибо!

etfreima 17.03.2022 02:46

@etfreima Я обновил весь пост

Fravadona 17.03.2022 10:08

Большое спасибо! Первоначально я принял это как ответ, поскольку смог заставить его работать, хотя я также смог заставить работать ответ @potong, и поскольку их ответ немного более актуален для прямого вопроса, в котором используется sed. В конечном счете, я согласен, что awk потенциально лучший инструмент здесь. Спасибо за отличный ответ и подробную информацию!

etfreima 17.03.2022 19:09
Ответ принят как подходящий

Это может сработать для вас (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-трюм (через пробел).

Сосредоточьтесь только на строках, содержащих multiconfig.

Замените текущую строку новыми целями.

Если текущая строка пуста, замените ее на [EMPTY], напечатайте, а затем удалите.

В противном случае удалите все цели, кроме первой, переключитесь на пространство удержания и подготовьтесь к следующей цели, удалив первую цель и следующее за ней пространство, затем переключитесь обратно на текущую строку и распечатайте ее.

Другие вопросы по теме