Использование sed для замены подстрок между определенными тегами

6 ответов

Во избежание путаницы я бы посоветовал вам использовать разделитель, отличный от /, для sed, если /присутствует в ваших паттернах. Я лично использую ~для таких случаев. Тот -лайнер, который вам нужен, выглядит так:

 sed '/<FilePath>/s~/~\\~g;/<\\FilePath>/s~<\\~</~' your_file

Пояснение :/<FilePath>/s~/~\\~g-заменяет /на \в строках, содержащих подстроку <FilePath>. Это заменит все /, так что </FilePath>станет <\FilePath>. Чтобы вернуть это, нужна вторая часть выражения-/<\\FilePath>/s~<\\~</~

0
18.03.2021, 23:06

Вот довольно простое решение, созданное с помощью конвейера с использованием sed и tr. Предполагается, что:

  • Внутри<FilePath>…</FilePath>(нет вложенных тегов, замена выполняется только до следующего <после<FilePath>).
  • <FilePath>не появляется внутри литеральной строки (нет <![CDATA[<FilePath>/blah]]>или<mytag label="<FilePath>">).
  • Ваша реализация sed правильно обрабатывает последнюю строку, даже если она не имеет завершающего символа новой строки.

Принцип заключается в использовании trдля переключения между <и разрывами строк; таким образом, когда sed обрабатывает «строку», он фактически обрабатывает текст между одним тегом открытия/закрытия и текстовым тегом открытия/закрытия.

tr '<\n' '\n<' | sed '/^FilePath>/ y:/:\\:' | tr '<\n' '\n<'

Вот Perl-решение. Предполагается, что:

  • Внутри нет вложенных тегов <FilePath>…</FilePath>.
  • <FilePath>не появляется внутри литеральной строки (нет <![CDATA[<FilePath>/blah]]>или<mytag label="<FilePath>">).
  • За
  • <FilePath>всегда следует </FilePath>в той же строке ).

Конструкция довольно естественна :она применяет функцию backslashifyк тексту внутри <FilePath>…</FilePath>. Регулярное выражение .*?— это не -жадное совпадение :, жадное совпадение .*заменит все от первого <FilePath>до последнего </FilePath>в строке, если в строке будет несколько <FilePath>…</FilePath>фрагментов. та же линия. s:(?!<<)/(?!>):\\:— это более изящная версия tr:/:\\:, которая позволяет избежать замены косых черт, если они идут непосредственно перед >или после <, что позволяет использовать вложенные теги.

perl -pe 'sub backslashify {local $_ = $_[0]; s:(?!<<)/(?!>):\\:; return $_} s:(<FilePath>)(.*?)(</FilePath>):$1.backslashify($2).$3:e'
0
18.03.2021, 23:06

команда

sed -e 's/\//\\/g' -e 's/<\\/<\//g' filename

выход

<FilePath>a\b\c\d</FilePath> 
<OtherTags>Bob</OtherTags>
<FilePath>1\2\3\4</FilePath>
1
18.03.2021, 23:06

С GNU awk для соответствия 3-го аргумента():

awk 'match($0,/(.*<FilePath>)(.*)(<\/FilePath>.*)/,a){ gsub("/","\\",a[2]); $0=a[1] a[2] a[3] } 1' file
<FilePath>a\b\c\d</FilePath>
<OtherTags>Bob/Smith</OtherTags>
<FilePath>1\2\3\4</FilePath>

Вышеописанное было выполнено с использованием этого ввода:

$ cat file
<FilePath>a/b/c/d</FilePath>
<OtherTags>Bob/Smith</OtherTags>
<FilePath>1/2/3/4</FilePath>
3
18.03.2021, 23:06

В GNU sed с расширенным режимом регулярных выражений мы можем постепенно сопоставлять открытие и закрытие тега xml FilePath в одной и той же строке и предполагать, что этот тег не является частью кавычек или комментариев.

sed -Ee ' :a;s|<(FilePath)>([^/]*(/[^/]*)*)/([^/]*</\1>)|<\1>\2\\\4|;ta' file
perl -lpe '
 s{<FilePath>\K.*?(?=</FilePath>)}
  <$& =~ tr|/|\\|r>xge;
' file

Мы изолируем часть между открытием и закрытием тега и преобразуем прямая косая черта на обратную косую черту в его части.

Мы можем составить многострочное регулярное выражение для простоты выражения намерения.

snr='
s|
  <(FilePath)>
   ( [^/]* ([/][^/]*)* )
          /
   ( [^/]* )
  </\1>
|<\1> \2 \\ \4 </\1>|
'
ws=$'\t \n'
sed -E ":a;${snr//[$ws]/};ta" file
1
18.03.2021, 23:06

Предполагая, что документ является правильно сформированным XML-документом, таким как

<?xml version="1.0"?>
<root>
  <FilePath>a/b/c/d</FilePath>
  <OtherTags>Bob</OtherTags>
  <FilePath>1/2/3/4</FilePath>
</root>

Затем, используя xmlstarlet, мы можем преобразовать прямую косую черту в обратную косую черту в значениях всех FilePathузлов:

$ xmlstarlet ed -u '//FilePath' -x 'translate(., "/", "\")' file.xml
<?xml version="1.0"?>
<root>
  <FilePath>a\b\c\d</FilePath>
  <OtherTags>Bob</OtherTags>
  <FilePath>1\2\3\4</FilePath>
</root>

Функция XPath translate()изменяет символы второго аргумента(/)на символы третьего аргумента(\)в строке, на которую ссылается первый аргумент (., значение текущего узла ). Функция translate()применяется ко всем узлам, соответствующим XPath //FilePath. Этот шаблон XPath соответствует узлам FilePathв любом месте всего документа.

1
18.03.2021, 23:06

Теги

Похожие вопросы