Итак, у меня есть файл конфигурации, который анализируется различными сценариями, поэтому формат нельзя изменить, хотя содержимое можно, если формат строго соблюдается. Этот файл по умолчанию содержит такие строки, как
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]»?
Я ценю любой вклад, который кто-либо может дать,
Спасибо всем заранее!
Предполагая, что вы пытаетесь сопоставить 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
печатает текущую строку.
@etfreima Я обновил весь пост
Большое спасибо! Первоначально я принял это как ответ, поскольку смог заставить его работать, хотя я также смог заставить работать ответ @potong, и поскольку их ответ немного более актуален для прямого вопроса, в котором используется sed. В конечном счете, я согласен, что awk потенциально лучший инструмент здесь. Спасибо за отличный ответ и подробную информацию!
Это может сработать для вас (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]
, напечатайте, а затем удалите.
В противном случае удалите все цели, кроме первой, переключитесь на пространство удержания и подготовьтесь к следующей цели, удалив первую цель и следующее за ней пространство, затем переключитесь обратно на текущую строку и распечатайте ее.
На самом деле это очень близко к моему желаемому решению, хотя, поскольку этот вопрос относится к методу sed, не могли бы вы объяснить логику? Спасибо!