SED команда для удаления косой черты “/” между двумя тегами

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

Я использую Linux и хочу использовать sed, чтобы удалить косую черту (/) между 2 тегами.

Из этого: input.xml

<file>/text</file>
<file>/text2</file>
<file>/text</file>

… в это: output.xml:

<file>text</file>
<file>text2</file>
<file>text</file>

Тестировал много кода без успеха
пример:

sed s'/<file>/s|^\.{1,2}/||' fileout

Можете помочь?

Имея корректный XML-файл, такой как

<?xml version="1.0"?>
<root>
  <file>/text</file>
  <file>/text2</file>
  <file>/text</file>
  <file>другой текст</file>
</root>

… вы можете использовать XMLStarlet, чтобы удалить первый символ значения каждого узла file, если значение начинается с /:

xmlstarlet edit \
    --update '//file[starts-with(text(), "/")]' \
    --expr 'substring(text(), 2)' \
    myfile.xml

Или, используя более короткий синтаксис,

xmlstarlet ed \
    -u '//file[starts-with(text(), "/")]' \
    -x 'substring(text(), 2)' \
    myfile.xml

Это находит каждый узел file во всем входном документе, значение которого начинается с /, а затем удаляет этот первый символ, используя substring().

Результат:

<?xml version="1.0"?>
<root>
  <file>text</file>
  <file>text2</file>
  <file>text</file>
  <file>другой текст</file>
</root>

Это (и ниже) будет работать с узлами, значения которых содержат встроенные переносы строк.


Если вы хотите обнаружить / в любом месте значения, а не только в начале, и если вы хотите удалить их все, вы можете сделать это, используя contains() и translate():

xmlstarlet edit \
    --update '//file[contains(text(), "/")]' \
    --expr 'translate(text(), "/", "")' \
    myfile.xml

Или просто (поскольку вызов translate() оставит значения без изменений, если в них не было /),

xmlstarlet edit \
    --update '//file' \
    --expr 'translate(text(), "/", "")' \
    myfile.xml

Учитывая этот входной файл:

<?xml version="1.0"?>
<root>
  <file>text/</file>
  <file>/text/2</file>
  <file>te/x/t/</file>
  <file>другой текст</file>
</root>

… вышеуказанная команда приведет к следующему:

<?xml version="1.0"?>
<root>
  <file>text</file>
  <file>text2</file>
  <file>text</file>
  <file>другой текст</file>
</root>

Входной (исправленный синтаксис) XML файл (пропущен закрывающий > на втором узле file):

<r>
<file>/text</file>
<file>/text2</file>
<file>/text</file>
</r>

С современным синтаксисом и правильной функцией XPath fn:replace() (что-то вроде sed для XPath, позволяя использовать регулярные выражения и группы захвата с XPath версии >= 2), используя XQuery, вы можете сделать следующее:

xidel --xquery '
    <r>{
        for $x in //file
        return <file>{replace($x, "^/(.*)", "$1")}</file> 
    }</r>
' --output-format=xml file.xml

Это даст:

<?xml version="1.0" encoding="UTF-8"?>
<r>
<file>text</file>
<file>text2</file>
<file>text</file>
</r>

Если вам нужно редактировать файл на лету, используйте sponge, утилиту из GNU more-utils:

xidel ... file.xml | sponge file.xml

Используя Raku (ранее известный как Perl_6)

…с поддерживаемым сообществом модулем XML для Raku:

~$ raku -MXML -e 'my  $xml = open-xml( $*ARGFILES.Str );
                  for $xml.elements( :RECURSE(0), :TAG{"file"} ) -> $E {
                      my $old = $E.contents[0];
                      my $new = XML::Text.new( text => $old.text.subst(/^ "/" /) );
                      $E.replace( $old, $new );
                  };  .say for $xml;'   file.xml

Raku – это язык программирования в семье Perl, который предлагает высокоуровневые грамматики для разбора текста. В приведенном выше примере используется его родной XML-Grammar движок, модуль XML в Raku разбирает входной XML файл. Таким образом, XML-элементы идентифицируются и могут быть перебраны.

Проблема решений только с использованием регулярных выражений (например, sed) для XML заключается в том, что замены tend tend дико: вы часто сталкиваетесь с трудностями в ограничении замен только определенными уровнями/тегами. В Raku с модулем XML вы можете, например, ограничить замены 1). верхним уровнем и 2). только внутри тега <file>. Это делается путем установки кода на итерацию через elements с ограничениями :RECURSE(0), :TAG{"file"}. Здесь также можно добавить :NEST, чтобы перебрать только EVEN узлы.

[ Не беспокойтесь, если вы хотите перебрать все TAG на любых уровнях: просто установите :RECURSE(Inf) и удалите именованный аргумент :TAG, который устанавливает ограничение :TAG на ложь ].

Таким образом, каждый внутренний (т.е. не теговый) contents[0] элемента назначается переменной $old, которая на самом деле является объектом XML::Text. Объект $old извлекается в строку через .text, и subst заменяется на пустую строку, чтобы удалить нежелательную косую черту "/". Создается новый (XML::Text.new) объект ($new) с исправленным text => 'value' парой ключ/значение. На этом этапе процедура replace модуля XML завершает работу: replace( $old, $new ).

Пример входных данных (спасибо @Kusalananda!):

<?xml version="1.0"?>
<root>
  <file>/text</file>
  <file>/text2</file>
  <file>/text</file>
  <file>другой текст</file>
</root>

Пример выходных данных (ведущая / удалена из тегов <file>):

<?xml version="1.0"?><root>
  <file>text</file>
  <file>text2</file>
  <file>text</file>
  <file>другой текст</file>
</root>

https://github.com/raku-community-modules/XML
https://raku.land/?q=XML
https://rakudo.org/
https://raku.org

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

Для решения задачи удаления символа косой черты (/) между тегами <file> в XML-файле с помощью утилиты sed в Linux, необходимо использовать соответствующий синтаксис для замены.

Исходя из вашего примера, содержащего следующие строки:

<file>/text</file>
<file>/text2</file>
<file>/text</file>

вы хотите получить:

<file>text</file>
<file>text2</file>
<file>text</file>

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

sed 's|<file>/\([^<]*\)</file>|<file>\1</file>|g' input.xml > output.xml

Объяснение команды:

  1. s|…|…|g – это основной шаблон замены, где s обозначает "замена", а g в конце означает "глобальная замена" (которая затрагивает все вхождения в строке).
  2. */([^<])** – это регулярное выражение, которое находит текст между тегами <file>, начинающийся с косой черты. Вот что оно означает:
    • <file>/ – ищем вхождение <file>/.
    • \([^<]*\) – захватываем (группируем) все символы, кроме <, следящие за /. Это значение будет подставлено в результат замены. Скобки обозначают группу.
    • </file> – ищем конец тега </file>.
  3. \1 – здесь мы заменяем найденный шаблон на <file> и содержимое группы \1, которая содержит текст без начальной косой черты, и закрывающий тег </file>.

Пример использования:

Если у вас есть файл input.xml со следующим содержимым:

<root>
  <file>/text</file>
  <file>/text2</file>
  <file>/text</file>
  <file>other text</file>
</root>

Вы можете выполнить команду:

sed 's|<file>/\([^<]*\)</file>|<file>\1</file>|g' input.xml > output.xml

После выполнения этой команды файл output.xml будет содержать:

<root>
  <file>text</file>
  <file>text2</file>
  <file>text</file>
  <file>other text</file>
</root>

Заключение

Таким образом, использование sed позволяет эффективно удалять символы / из содержимого между тегами в XML-документе. Обратите внимание, что регулярные выражения в sed могут быть чувствительными к изменениям и интерпретации, так что всегда полезно протестировать команду на небольшом наборе данных перед применением.

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

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