sed を使用して特定のタグ間の部分文字列を置き換える

sed を使用して特定のタグ間の部分文字列を置き換える

2つのsedコマンドを1つにまとめたいのですが、どうすればいいかわかりません。いくつかの方法を試しましたが、成功しませんでした。

その結果、特定のトークン間のすべてのスラッシュをバックスラッシュに置き換えようとします。その結果は次のとおりです。

源泉:

<FilePath>a/b/c/d</FilePath> 
<OtherTags>Bob</OtherTags>
<FilePath>1/2/3/4</FilePath>

結果:

<FilePath>a\b\c\d</FilePath>
<OtherTags>Bob</OtherTags>
<FilePath>1\2\3\4</FilePath>

ラベル間のテキストを変更する次のコマンドが見つかりました。

sed -i -e 's/\(<FilePath>\).*\(<\/FilePath>\)/<FilePath>TEXT_TO_REPLACE_BY<\/FilePath>/g' test.txt

しかし、このコマンドはすべてのテキストを置き換えます...だから私が望むのはテキストを保持し、次のコマンドを使用してスラッシュをバックスラッシュに置き換えることです。

sed -e 's/\\/\//g' test.txt

しかし、これら2つを組み合わせるのは難しいです。

ご協力ありがとうございます。

答え1

match()の3番目の引数を一致させるには、GNU awkを使用します。

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>

答え2

注文する

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

出力

<FilePath>a\b\c\d</FilePath> 
<OtherTags>Bob</OtherTags>
<FilePath>1\2\3\4</FilePath>

答え3

拡張正規表現パターンで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

答え4

これはsedとtrを使用してパイプを介して構築された非常にシンプルなソリューションです。以下を想定します。

  • 内部に入れ子になったタグはありません(代替は次の後<FilePath>…</FilePath>にのみ実行されます)。<<FilePath>
  • <FilePath>リテラル文字列(no<![CDATA[<FilePath>/blah]]>または<mytag label="<FilePath>">)内には表示されません。
  • 最後の行に終了行の改行がない場合でも、sed実装はこれを正しく処理します。

アイデアはtr改行<と改行を使用することです。このようにして、sedが「行」を処理すると、実際にはオープン/クローズタグとテキストオープン/クローズタグの間のテキストが処理されます。

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

これはPerlソリューションです。以下を想定します。

  • 内部に入れ子になったタグはありません<FilePath>…</FilePath>
  • <FilePath>リテラル文字列(no<![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'

関連情報