Как я могу сопоставить и удалить первый и второй шаблон в тегах xml, используя sed или awk?

Вот пример

<data>A78-1-1134-HI-1</data>
<data>T78-12-1346-AG-2</data>
<data>G78-4-2156-Ag-6</data>
<data>A78-10-1971-Hh-10</data>

Вот результат, который я пытаюсь получить:

<data>1134</data>
<data>1346</data>
<data>2156</data>
<data>1971</data

Можно ли это сделать в одну строку? Вот что я попробовал:

sed 's/^.*<data>[[:alnum:]]-[0-9]-/<data>/g;s/-[a-Z].*<\/data>$//g'

Или удаляя только первый шаблон, когда я использую sed для печати, он работает:

sed -n 's/^.*<data>.*[[:alnum:]]-[0-9]-/<data>/p' file.xml | grep data

Но тогда эта команда не будет работать:

sed 's/^.*<data>.*[[:alnum:]]-[0-9]-/<data>/' file.xml

3 ответа3

1

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

В XSLT 2.0 это тривиальное преобразование.

<xsl:template match="data">
  <xsl:copy>
    <xsl:value-of select="tokenize(., '-')[3]"/>
  </xsl:copy>
</xsl:template>
0

Похоже, что ваши повторения не указаны правильно. Также мне проще использовать подвыражения для извлечения подстрок. Я не знаю ваших точных спецификаций для сопоставления данных, но это работает для ваших примеров данных в вопросе (я думаю, что это POSIX-совместимый):

sed 's/<data>[[:alnum:]]\{1,\}-[0-9]\{1,\}-\([0-9]\{1,\}\)-[[:alnum:]]\{1,\}-[0-9]\{1,\}/<data>\1/' file.xml

Если в вашем распоряжении GNU sed , вы можете воспользоваться его расширениями для расширенных регулярных выражений для более простого выражения:

sed -r 's/^.*<data>[[:alnum:]]+-[0-9]+-([0-9]+)-[[:alnum:]]+-[0-9]+/<data>\1/' file.xml
0

Вот несколько решений:

  1. Если ваш файл действительно такой же простой, как ваш пример, вы можете сделать это с помощью gawk . Это предполагает, что ваш файл состоит только из записей data как описано в вашем вопросе.

    gawk -F"-" '{print "<data>"$3"</data>"}' file.xml
    
    • -F"-" говорит gawk взять - в качестве разделителя полей скрипт затем печатает 3-е поле.


  2. Для немного более сложных файлов, которые содержат ненужные строки, он будет напечатан, только если первое ($1~/data/) и последнее ($NF~/data/) поля содержат data:

    gawk -F"-" '($1~/data/ && $NF~/data/){print "<data>"$3"</data>"}' file.xml
    
  3. Если в вашем файле может быть много записей <data> и вы заботитесь только о тех, которые выглядят как A1-2B-C3-4D:

    perl -ne '/(<data>).+?-.+?-(.+?)\-.+(<\/data>)/ && do{print "$1$2$3\n"}' file.xml
    

    -ne означает применять этот скрипт к каждой строке входного файла. В Perl (и многих других инструментах) круглые скобки позволяют захватывать совпадения регулярных выражений. Здесь я собираю три шаблона: открывающий и закрывающий теги ($1 и $3), поэтому мне не нужно вводить их дважды и шаблон, который мы ищем, $2 .

    Если вам нужно быть более конкретным, используйте это, чтобы разрешить только буквенно-цифровые символы в первом поле и только цифры в остальных:

    perl -ne '/(<data>)[\w\d]+?-\d+?-(\d+?)\-.+(<\/data>)/ && do{print "$1$2$3\n"}' file.xml
    
  4. Все это предполагает, что ваши теги <data> и </data> находятся на одной строке. Если это не так, вы можете сделать что-то вроде этого:

    perl -ne '
     $d++ if /<data>/; 
      /[\w\d]+?-\d+?-(\d+?)\-.+/ && do{
                 print "<data>$1</data>\n" if $d>0
            }; 
     $d-- if /<\/data>/; 
    ' file.xml
    

    $d будет положительным, если мы находимся в тегах <data></data> . Если мы найдем строку, соответствующую регулярному выражению, напечатайте.


ОБНОВИТЬ:

Если вы хотите отредактировать файл, не только распечатать его содержимое, но и фактически изменить исходный файл, используйте это:

perl -i -ne 's/(<data>).+?-.+?-(.+?)\-.+(<\/data>)/$1$2$3/; print' file.xml

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