コマンドラインから(入れ子になった)テキスト引用符を削除する

コマンドラインから(入れ子になった)テキスト引用符を削除する

コマンドラインで大量のテキストを解析し、すべての(ネストされた)テキスト引用符を空白に置き換える必要があります。引用符は特定の構文で表示されます[quote=username]quoted text[/quote]

入れ子になった引用符を含む入力の例は次のとおりです。

text part 1 [quote=foo] outer quote 1 [quote=bar] inner quote [/quote] outer quote 2 [/quote] text part 2 [quote=foo-bar] next quote [/quote] text part 3

予想される出力は次のとおりです。

text part 1   text part 2   text part 3

の助けを借りてこの問題どういうわけか動作させましたが(上記の出力を得ました)、sed ':b; s/\[quote=[^]]*\][^[\/]*\[\/quote\]/ /g; t b'中間部分( ]には引用符または同じ文字を[^[\/]含めることができるため問題があります。[]

つまり、sed入力がたとえば次のような場合、私のコマンドは機能しません。

text part 1 [quote=foo] outer quote 1 [quote=bar] inner quote [foo] [/quote] outer quote 2 [/quote] text part 2 [quote=foo-bar] next quote [/quote] text part 3

1つの問題は、sed貪欲な修飾子をサポートしていないようで、可能な限り長い一致が常に入力からキャプチャされることです。扱いにくいㅏ)ユーザー名と雨)一般的な引用。

私はこれがsedこの問題のための最善のツールではなく、そのようなことをすることができないかもしれないと思います。たとえば、おそらく。perlそれともawkもっとうまくいくでしょうか?

今最後の質問は、この問題を解決するための最良かつ効率的な方法は何ですか?

答え1

入力に文字が含まれていないことがわかっている場合は、次のことができます<>

sed '
  # replace opening quote with <
  s|\[quote=[^]]*\]|<|g
  # and closing quotes with >
  s|\[/quote\]|>|g
  :1
    # work our way from the inner quotes
    s|<[^<>]*>||g
  t1'

<または、文字を含めることができる場合は、>次のスキームを使用してエスケープできます。

sed '
  # escape < and > (and the escaping character _ itself)
  s/_/_u/g; s/</_l/g; s/>/_r/g

  <code-above>

  # undo escaping after the work has been done
  s/_r/>/g; s/_l/</g; s/_u/_/g'

AND perl、再帰正規表現の使用:

perl -pe 's@(\[quote=[^\]]*\](?:(?1)|.)*?\[/quote\])@@g'

またはあなたが言及したように:

perl -pe 's@(\[quote=.*?\](?:(?1)|.)*?\[/quote\])@@g'

を使用すると、perlオプションを追加して複数行の入力を処理できます-0777。の場合、sedコードの前に次のプレフィックスを追加する必要があります。

:0
$!{
  N;b0
}

これにより、入力全体がパターン空間にロードされます。

答え2

私はこれを確認し、それは私のために働いた。代わりに別の一時モードを選択することもできますfoobar。このモードがないと、タグ間sedのすべてのエントリが削除され、1つだけ残ります。text part 1 text part 3

sed -e 's/\/quote\]/foobar\]/3' -e 's/\[.*\/quote\]//' -e 's/\[.*foobar]//' testfile

代わりtestfileにパイプすることができればcat

答え3

すべての開始引用符でカウンター変数を増やし、すべての閉じる引用符でカウンター変数を減らす小さなスクリプトです。カウンタ変数が大きい場合は、0テキストの断片をスキップします。

#!/bin/bash

# disable pathname expansion
set -f    
cnt=0
for i in $(<$1); do
        # start quote
        if [ "${i##[quote=}" != "$i" ] && [ "${i: -1}" = "]" ]; then
                ((++cnt))
        elif [ "$i" = "[/quote]" ]; then
                ((--cnt))
        elif [ $cnt -eq 0 ]; then
                echo -n "$i "
        fi
done
echo

出力:

$ cat q1
text part 1 [quote=foo] outer quote 1 [quote=bar] inner quote [/quote] outer quote 2 [/quote] text part 2 [quote=foo-bar] next quote [/quote] text part 3
$ ./parse.sh q1
text part 1 text part 2 text part 3
$ cat q2
text part 1 [quote=foo] outer quote 1 [quote=bar] inner quote [foo] [/quote] outer quote 2 [/quote] text part 2 [quote=foo-bar] next quote [/quote] text part 3
$ ./parse.sh q2
text part 1 text part 2 text part 3

答え4

POSIX sedここの詳細な指示に従ってこれを行うことができます。このソリューションは、表示された両方の入力に適用されます。必要な変換を達成するために改行をマーカーとして使用するため、入力制限は複数行ではありません。

$ sed -e '
      :top
      /\[\/quote]/!b
      s//\
&/
      s/\[quote=/\
\
&/

     :loop
        s/\(\n\n\)\(\[quote=.*\)\(\[quote=.*\n\)/\2\1\3/
     tloop

     s/\n\n.*\n\[\/quote]//
     btop
 '  input.txt

関連情報