2つの文字列の間に指定された文字を置き換えますか?

2つの文字列の間に指定された文字を置き換えますか?

仕上げる事があります。 &内部または間のすべての項目を#に置き換える必要があります<ex> </ex>。実際の例は次のとおりです。

a & b & c <ex> a & b & c </ex> a & b & c

<ex>繰り返しますが、&内部と以前のすべての項目を置き換える必要があります。</ex>

予想出力:

a & b & c <ex> a # b # c </ex> a & b & c

これを行う方法の説明を投稿してください。

編集#1

sedAS400システムで実行する予定なので、Perlや他のインタプリタをインストールできないので、ソリューションを提供してください。

答え1

<ex>...</ex>各行が一度だけ表示される場合:

sed -e :1 -e 's@\(<ex>.*\)&\(.*</ex>\)@\1#\2@;t1'

複数の項目がある可能性があり、入れ子になっていない場合(または入れ子になって&最も深い項目のみを置き換えたい場合):

sed '
  s|_|_u|g        # replace all underscores with "_u"
  s|(|_o|g        # replace all open parentheses with "_o"
  s|)|_c|g        # replace all close parentheses with "_c"
  s|<ex>|(|g      # replace all open ex tags with "("
  s|</ex>|)|g     # replace all close ex tags with ")"

  :1              # a label

  s/\(([^()]*\)&\([^()]*)\)/\1#\2/g
                  # find:
                  #   an open parentheses, 
                  #   some non-parentheses chars (captured),
                  #   an ampersand, 
                  #   some non-parentheses chars (captured) and 
                  #   a close parentheses, 
                  # replace with
                  #   the first captured text, 
                  #   an octothorpe
                  #   the second captured text, 
                  # globally in the current record.

  t1              # if there was a successful replacement, goto label "1",
                  # else carry on

  s|(|<ex>|g      # restore open tags
  s|)|</ex>|g     # restore close tags
  s|_o|(|g        # restore open parentheses
  s|_c|)|g        # restore close parentheses
  s|_u|_|g        # restore underscores
'

入れ子にすることができ、それを囲むものに置き換えたい場合:

sed '
  s|_|_u|g;s|(|_o|g;s|)|_c|g
  s|<ex>|(|g;s|</ex>|)|g;:1
  s/\(([^()]*\)(\([^()]*\))\([^()]*)\)/\1_O\2_C\3/g;t1
  :2
  s/\(([^()]*\)&\([^()]*)\)/\1#\2/g;t2
  s|(|<ex>|g;s|)|</ex>|g
  s|_O|<ex>|g;s|_C|</ex>|g
  s|_o|(|g;s|_c|)|g;s|_u|_|g'

答え2

Perl(バージョン5.14が必要)を使用して、次の操作を行います。

perl -pe 's%(<ex>.*?</ex>)% $1 =~ s/&/#/gr %eg'

以前のバージョンでは、より冗長でなければなりませんでした。

perl -pe 's%(<ex>.*?</ex>)% ($_x = $1) =~ s/&/#/g; $_x %eg'

説明:<ex>タグ間のすべての内容をに入れて$1$1を#に置き換えます。

答え3

別のPerlコマンド、

$ perl -pe 's/&(?=(?:(?!<ex>|<\/ex>).)*<\/ex>)/#/g' file
a & b & c <ex> a # b # c </ex> a & b & c

上記のコマンドを説明する前に、Negative LookaheadとPositive Lookaheadが実際に何をしているかを説明します。

正規表現は(?=...)肯定的な予測を意味します。ツアー(たとえば、肯定的および否定的なプレビュー、肯定的および否定的なプレビュー)は、幅がゼロの一致を作成します。つまり、どの文字とも一致しません。通常、肯定的および否定的な予測は、ヘルスチェックの目的で使用されます。(?:...)非キャプチャグループとも呼ばれます。つまり、非キャプチャグループ内のパターンのみが一致します。どの文字もキャプチャしません。

  • (?!<ex>|<\/ex>)文字列<ex>または.と一致できません</ex>
  • (?:(?!<ex>|<\/ex>).)実際に意味するのは、最初に次の3〜4文字を見つけ、次の3〜4文字が不可能かどうかを確認する<ex>ことです</ex>.この条件が満たされると、次の文字のみが一致します。
  • (?:(?!<ex>|<\/ex>).)*文字列<ex>またはが検出されるまで、上記の手順を0回以上実行します。</ex>これら 2 つの文字列のいずれかが見つかると、突然次の文字の一致が停止します。

  • (?:(?!<ex>|<\/ex>).)*<\/ex>次の</ex>文字列とも一致します。これはすべて予測に反映されます。

  • &(?=(?:(?!<ex>|<\/ex>).)*<\/ex>)&最後に、上記の条件を満たす文字が次の場合にのみ文字と一致します。つまり、&次に属さない文字が来るか、閉じるマークが0回以上来なければ<ex>なりません。</ex></ex>

答え4

入れ子になっていない行で複数回発生すると役に立ちます。

#cat plop
>a & b & c <ex> a & b & c </ex> a & b & c <ex> a & b & c </ex> a & b & c

#cat plop |sed -e :1 -e 's@\(<ex>[^(</ex>)]*\)&\(.*</ex>\)@\1+\2@;t1'     
>a & b & c <ex> a + b + c </ex> a & b & c <ex> a + b + c </ex> a & b & c

関連情報