Замените значение атрибута XML значением переменной оболочки.

Вопрос или проблема

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE POWERMART SYSTEM "powrmart.dtd">
<POWERMART CREATION_DATE="12/01/2016 17:43:15" REPOSITORY_VERSION="184.93">
<REPOSITORY NAME="PCREPO_BIDEV" VERSION="184" CODEPAGE="UTF-8" 
DATABASETYPE="Oracle">
<FOLDER NAME="ABC" GROUP="" OWNER="Administrator" SHARED="SHARED" 
DESCRIPTION="" PERMISSIONS="rwx---r--" UUID="3b13d2c9-39dc-426f-8320- 
def2bb8424ef">

У меня есть вышеуказанные образцы данных. В каком файле будет находиться FOLDER NAME с некоторым значением. Мне нужно заменить его на другое значение, которое представляет собой переменную. Я хочу заменить FOLDER NAME=”ABC” на DEF.

 sed -i "s/<FOLDER NAME=\"\*\"/<FOLDER NAME=\"$FLDR\"/g" Gather.XML

Вышеуказанная команда sed не возвращает ошибок, но она не производит замену.

Предполагая, что это корректный XML-документ, используя XMLStarlet:

xmlstarlet ed -u '/POWERMART/REPOSITORY/FOLDER[@NAME="ABC"]/@NAME' -v "$FLDR" file.xml

Это найдет узел FOLDER под /POWERMART/REPOSITORY, у которого атрибут NAME равен ABC, и изменит его значение на значение переменной оболочки FLDR.

Это также будет работать, если между именем узла FOLDER и атрибутом NAME будет перенос строки, так как XML нечувствителен к такому пробелу.

Пример:

$ cat file.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE POWERMART SYSTEM "powrmart.dtd">
<POWERMART CREATION_DATE="12/01/2016 17:43:15" REPOSITORY_VERSION="184.93">
  <REPOSITORY NAME="PCREPO_BIDEV" VERSION="184" CODEPAGE="UTF-8" DATABASETYPE="Oracle">
    <FOLDER NAME="ABC" GROUP="" OWNER="Administrator" SHARED="SHARED" DESCRIPTION="" PERMISSIONS="rwx---r--" UUID="3b13d2c9-39dc-426f-8320-def2bb8424ef"/>
  </REPOSITORY>
</POWERMART>

$ FLDR='DEF'
$ xmlstarlet ed -u '/POWERMART/REPOSITORY/FOLDER[@NAME="ABC"]/@NAME' -v "$FLDR" file.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE POWERMART SYSTEM "powrmart.dtd">
<POWERMART CREATION_DATE="12/01/2016 17:43:15" REPOSITORY_VERSION="184.93">
  <REPOSITORY NAME="PCREPO_BIDEV" VERSION="184" CODEPAGE="UTF-8" DATABASETYPE="Oracle">
    <FOLDER NAME="DEF" GROUP="" OWNER="Administrator" SHARED="SHARED" DESCRIPTION="" PERMISSIONS="rwx---r--" UUID="3b13d2c9-39dc-426f-8320-def2bb8424ef"/>
  </REPOSITORY>
</POWERMART>

Если вам нужно сопоставить определенное NAME REPOSITORY, то, например:

xmlstarlet ed -u '/POWERMART/REPOSITORY[@NAME="PCREPO_BIDEV"]/FOLDER[@NAME="ABC"]/@NAME' -v "$FLDR" file.xml

Попробуйте это,

sed -i 's/<FOLDER NAME="[A-Z]*"/<FOLDER NAME="'$FLDR'"/g' Gather.XM

В вашем выражении sed не хватает “.“, чтобы сопоставить все символы

 sed -i "s/<FOLDER NAME=\"\.*\"/<FOLDER NAME=\"$FLDR\"/g" Gather.XML

Похоже, вам следует использовать [^\"]* вместо \*:

sed -i "s/<FOLDER NAME=\"[\"]*\"/<FOLDER NAME=\"$FLDR\"/g" Gather.XML

Поскольку вы хотите сопоставить весь текст до первого ". \* будет сопоставлять символ * в буквальном смысле, но его там нет, так что содержимое файла никогда не будет соответствовать.

Я не рекомендую sed в общем случае, но в этом случае это, возможно, достаточно просто для его обработки, нам просто нужно быть осторожными с некоторыми опасностями:

  • если $FLDR содержит ", то это сломает XML.

  • Если $FLDR содержит / (что кажется правдоподобным для папки), то это сломает команду sed.

  • Если $FLDR содержит \ или &, это может привести к добавлению sed неожиданных символов или строк в XML.

(Не говоря уже о том, что если $FLDR не обернуто в двойные кавычки, могут возникнуть дополнительные проблемы.)

Каждый из этих элементов может быть использован злонамеренно для изменения XML неожиданным образом.

Если вы экранируете символы \, / и &, а также экранируете " с помощью &quot; (\&quot; для sed), вы сможете обойти эти проблемы.

Вы можете выполнять замены в bash с помощью "${FLDR//a/b}", хотя для обратных и прямых косых черт и амперсандов потребуется дополнительное экранирование. И обратите внимание, что нужно быть осторожным с порядком, в котором вы выполняете замены, и очень осторожным с экранированием в самих подстановках bash.

Другой проблемой в вашем коде является то, что поиск "…NAME=\"\*\"…" будет соответствовать только буквальной строке …"*"….

Вы можете заменить это на "…\".*\"…" (любые символы между кавычками).
Или на "…\"[^"]*\"…" (любые символы, не являющиеся кавычками, между кавычками), если вы не хотите беспокоиться о том, насколько “жадным” будет совпадение.

Это должно защитить от всех вышеперечисленных случаев:

FLDR="${FLDR//\\/\\\\}"     # экранировать обратные слеши (прежде всего)
FLDR="${FLDR//\"/\&quot;}"  # экранировать кавычки (прежде чем экранировать амперсанд)
FLDR="${FLDR//&/\\&}"       # экранировать амперсанды
FLDR="${FLDR//\//\\/}"      # экранировать прямые косые черты

sed -i "s/<FOLDER NAME=\"[^\"]*\"/<FOLDER NAME=\"$FLDR\"/g" Gather.XML

Ответ или решение

Для замены значения атрибута XML с помощью переменной оболочки вы можете использовать утилиты, такие как sed или xmlstarlet. Рассмотрим оба метода и их особенности, а также потенциальные проблемы, которые могут возникнуть при их использовании.

Метод 1: Использование sed

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

  1. Объявление переменной:
    Убедитесь, что ваша переменная FLDR инициализирована, например:

    FLDR='DEF'
  2. Замена с использованием sed:
    Чтобы заменить значение атрибута NAME элемента FOLDER, вы можете использовать следующий синтаксис:

    sed -i "s/<FOLDER NAME=\"[^\"]*\"/<FOLDER NAME=\"$FLDR\"/g" Gather.XML

    Здесь регулярное выражение <FOLDER NAME=\"[^\"]*\" позволяет найти атрибут NAME с любым значением, заключенным в кавычки. Ключевое важно — это использование [^"], которое означает "любые символы, кроме кавычки".

  3. Проблемы с sed:

    • Если переменная FLDR содержит специальные символы, такие как &, /, \, или ", они могут сломать команду. Рекомендуется предварительно экранировать эти символы:
    FLDR=$(printf '%s\n' "$FLDR" | sed -e 's/[\/&]/\\&/g' -e 's/"/\\"/g')

Метод 2: Использование xmlstarlet

xmlstarlet — это инструмент, специально разработанный для обработки XML и может быть более надежным в этом контексте.

  1. Команда для замены:
    Замена значения атрибута NAME для элемента FOLDER может выглядеть следующим образом:

    xmlstarlet ed -u '/POWERMART/REPOSITORY/FOLDER[@NAME="ABC"]/@NAME' -v "$FLDR" Gather.XML

    Эта команда изменяет значение атрибута NAME, если текущее значение равно ABC.

  2. Преимущества xmlstarlet:

    • Учитывает структуру XML и не зависит от текстового формата, как sed.
    • Минимизирует риск повреждения документа из-за неправильного экранирования символов.

Заключение

Хотя sed может выполнять простые задачи, использование xmlstarlet рекомендуется для работы с XML-документами, чтобы избежать возможных ошибок, связанных с неформатированием. В любом случае, вы должны быть осторожны с содержимым переменной FLDR, особенно при наличии специальных символов, которые могут нарушить форматирование документа.

Подходите к обработке XML аккуратно, особенно в производственной среде, чтобы избежать повреждения ваших данных.

Оцените материал
Добавить комментарий

Капча загружается...