Я хочу изменить символы в файле XML с помощью sed. Вход выглядит так:

<!-- Input -->
<root>
  <tree foo="abcd" bar="abccdcd" />
  <dontTouch foo="asd" bar="abc" />
</root>

Теперь я хочу изменить все c на X в теге bar элемента дерева.

<!-- Output -->
<root>
  <tree foo="abcd" bar="abXXdXd" />
  <dontTouch foo="asd" bar="abc" />
</root>

Как правильная команда sed? Пожалуйста, учтите, что в одном теге может быть более одного вхождения c (рядом друг с другом или нет) ...

Я попробовал это сам, но это не изменит несколько C, и он добавляет X :(

sed -i 's/\(<tree.*bar=\".*\)c\(.*\"\/>\)/\1X\2/g' Input.xml

Редактировать: некоторые подробности;)

  • Это единовременная работа, после изменения документа я больше не буду к ней прикасаться

  • Структура так же просто, как указано выше. Это означает, что я могу захватить все строки (это работает) с:

    cat input.xml | grep ""

Итак, предположим, что у меня извлечена правильная строка, и я знаю, куда ее записать после модификации: Как изменить 'abcdeccd' на 'abXdeXXd'? Это на самом деле не проблема XML, а проблема регулярных выражений, или я здесь не прав?

2 ответа2

3

Как сказал RedGrittyBrick, лучший способ сделать это - использовать синтаксический анализатор XML, выбрать элемент, перевести символы, а затем записать его обратно, используя библиотеку XML. Это не принесет вам неприятных сюрпризов, оно выдержит испытание временем и т.д. Это не только лучше, но и намного превосходит другие вещи. Другие решения более или менее мгновенно становятся кошмарами для отладки, и, конечно же, повсюду будут скрытые проблемы.

Если это простая задача, которую нужно выполнить один раз, и которая очень осторожна, и кто-то проверяет результат и т.д., Т. Д. И т. П., То может быть меньше работы, чтобы сделать это плохим способом. Но когда- нибудь вы удивитесь, если вы сделаете это привычкой.

Например, вот один из плохих способов, которые, кажется, работают, но он опирается не только на действительный XML, но и на более или менее точный синтаксис, который вы описали ранее, который является просто подмножеством действительного XML, и, следовательно, действительный XML, безусловно, является возможность сделать код неудачным (что если кто-то добавит знак «>» в один из тегов? Добавьте особый случай. Что делать, если кто-то не использует кавычки? Добавить специальный случай и т.д.). Это проблема не использования реального парсера. Ниже была предпринята некоторая осторожность, чтобы вести себя как минимум псевдопарсер, читая тег, затем воздействуя на него, затем записывая его обратно, но для этого есть готовые инструменты, которые были тщательно протестированы.

#!/bin/sh
IFS='\n'
while read i; do
    if $(printf -- "${i}" | grep -qE '<tree [^>]+ bar="[^'"${1}"'"]*'"${1}"); then
        ORIGTAG=$(printf -- "${i}" | sed 's#^.*<tree [^>]\+ bar="\([^"]\+\)".*$#\1#g')
        NEWTAG=$(printf -- "${ORIGTAG}" | tr "${1}" "${2}")
        printf -- "${i}\n" | sed 's#\(^.*<tree [^>]\+ bar="\)'"${ORIGTAG}"'\(".*$\)#\1'"${NEWTAG}"'\2#g'
    else
        printf -- "${i}\n"
    fi
done < "${3}"

Использование: script.sh [символ для замены] [символ замены] [имя файла], например

script.sh c X myfile

IFS устанавливает "внутренний разделитель полей" в оболочке на новую строку, чтобы оставить пробелы в начале строк.

while read читает входной файл (заданный в качестве аргумента 3 для скрипта) построчно.

grep проверяет, находится ли конкретный тег в текущей строке И если тег содержит символ для перевода. Если это так, перейдите к sed логики; если нет, вернуть строку как есть.

sed выбирает старый тег, запускает на нем перевод символов и возвращает строку с новым тегом.

Как видите, никто не хотел бы найти этот скрипт и отладить его. Если это что - то другое , чем работа единовременного, не делают это так. Для здравомыслия будущих наблюдателей.

1

Это может работать для вас (GNU sed?):

sed '/^\s*<tree.*\<bar="/!b;s//&\n/;:a;s/\n\([^c"]\+\)/\1\n/;ta;s/\nc/X\n/;ta;:b;s/\n//' XML

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .