ファイル内の 2 つのマーカー間のすべてを削除するにはどうすればよいですか?

ファイル内の 2 つのマーカー間のすべてを削除するにはどうすればよいですか?

\{{[}テキストファイルの中に、とのような文字列の間にあるすべてのもの{]}\}(これらの文字列自体も含む)を削除したいテキストがあります。これらの2つの文字列できる同じ線上にある場合もあれば、異なる線上にある場合もあります。どちらの場合も、 の上先頭行の\{{[}テキストを削除する場合、その前、つまり左側のテキストは削除したくありません。これは、それ以降のテキストについても同様です{]}\}

例を挙げます。次のような内容のテキストファイルがあるとします。

Bla Bla bla bla \{{[} more bla bla
even more bla bla bla bla. 

A lot of stuff might be here.

Bla bla {]}\} finally done.

Nonetheless, the \{{[} show {]}\} goes on.

スクリプトは、コンテンツを含む別のテキストファイルを返す必要があります。

Bla Bla bla bla  finally done.

Nonetheless, the  goes on.

残念ながら、この簡単そうに見えるタスクは私には難しすぎましたsedどれでも標準の Linux マシンに何もインストールする必要がない限り (C と一部の Java は既にインストールされています)、どの言語でもソリューションを提供できます。

答え1

perl

perl -0777 -pe 's/\Q\{{[}\E.*?\Q{]}\}\E//gs'

入力全体が処理される前にメモリにロードされることに注意してください。

\Qsomething\Esomething正規表現ではなくリテラル文字列として扱われます。

通常のファイルをインプレースで変更するには、次の-iオプションを追加します。

perl -0777 -i -pe 's/\Q\{{[}\E.*?\Q{]}\}\E//gs' file.txt

GNUawkまたはmawk:

awk -v 'RS=\\\\\\{\\{\\[}|\\{\\]}\\\\}' -v ORS= NR%2

そこで、私たちはレコードセパレータ開始マーカーまたは終了マーカーのいずれかとして使用します (ここでは と のみがgawk正規表現としてmawkサポートされます)。ただし、正規表現演算子の文字 (バックスラッシュ、 、 ) と、 の引数では特別な文字であるため ( 、 ...などに使用)、バックスラッシュをもう一度エスケープする必要があります。そのため、多数のバックスラッシュが使用されます。RS{[-v\n\b

次に必要なのは、1 つおきのレコードを印刷することだけです。奇数レコードごとに (true)NR%2になります。1

どちらのソリューションでも、マーカーが一致しており、それらのセクションがネストされていないことを前提としています。

GNU の最新バージョンでファイルをその場で変更するにはawk-i /usr/share/awk/inplace.awk¹ オプションを追加します。


¹使ってはいけません-i inplaceは、まず現在の作業ディレクトリから拡張機能(または)gawkをロードしようとしますが、そこに誰かがマルウェアを仕掛けている可能性があります。で提供される拡張機能のパスはシステムによって異なる場合があります。inplaceinplaceinplace.awkinplacegawkgawk 'BEGIN{print ENVIRON["AWKPATH"]}'

答え2

sed   -e:t -e'y/\n/ /;/\\{{\[}/!b'               \
      -e:N -e'/\\{{\[.*{\]}\\}/!N'               \
           -e's/\(\\{{\[}\).*\n/\1/;tN'          \
           -e'y/ /\n/;s/\\{{\[}/& /;ts'          \
      -e:s -e's/\(\[} [^ ]*\)\({\]}\\}\)/\1 \2/' \
      -ets -e's/..... [^ ]* .....//;s/ //g;bt'   \
<<""
#Bla Bla {]}\} bla bla \{{[} more bla bla
#even more bla bla bla bla. \{{[} 
#
#A lot of stuff might be here.
#hashes are for stupid syntax color only
#Bla bla {]}\} finally {]}\} done.
#
#Nonetheless, the \{{[} show {]}\} goes \{{[} show {]}\} on.

#Bla Bla {]}\} bla bla  finally {]}\} done.
#
#Nonetheless, the  goes  on.

しかし、もっと良い方法があります。置換ははるかに少なく、置換が行われるのは、常に行われるのではなく、一度に 2、3 文字だけです.*。実際に.*使用されるのは、最初に出現する開始文字が最初に続く終了文字と確実にペアになっているときに、パターン スペースから間のスペースをクリアするときだけです。残りの時間はすべて、次に出現する区切り文字に到達するのに必要なだけ削除するだけsedです。don がそれを教えてくれました。D

sed -etD -e:t -e'/\\{{\[}/!b'  \
    -e's//\n /;h;D'       -e:D \
    -e'/^}/{H;x;s/\n.*\n.//;}' \
    -ett    -e's/{\]}\\}/\n}/' \
    -e'/\n/!{$!N;s//& /;}' -eD \
<<""
#Bla Bla {]}\} bla bla \{{[} more bla bla
#even more bla bla bla bla. \{{[} 
#
#A lot of stuff might be here.
#hashes are for stupid syntax color only
#Bla bla {]}\} finally {]}\} done.
#
#Nonetheless, the \{{[} show {]}\} goes \{{[} show {]}\} on.

#Bla Bla {]}\} bla bla  finally {]}\} done.
#
#Nonetheless, the  goes  on.

ただし、 RHS \newline エスケープは、リテラルのバックスラッシュでエスケープされた改行に置き換える必要がある場合があります。

より一般的なバージョンは次のとおりです。

#!/usr/bin/sed -f
####replace everything between START and END
   #branch to :Kil if a successful substitution
   #has already occurred. this can only happen
   #if pattern space has been Deleted earlier
    t Kil
   #set a Ret :label so we can come back here
   #when we've cleared a START -> END occurrence
   #and check for another if need be
    :Ret
   #if no START, don't
    /START/!b
   #sigh. there is one. get to work. replace it
   #with a newline followed by an S and save
   #a copy then Delete up to our S marker.
    s||\
S|
    h;D
   #set the :Kil label. we'll come back here from now
   #on until we've definitely got END at the head of
   #pattern space.
    :Kil
   #do we? 
    /^E/{
       #if so, we'll append it to our earlier save
       #and slice out everything between the two newlines
       #we've managed to insert at just the right points        
        H;x
        s|\nS.*\nE||
    }
   #if we did just clear START -> END we should
   #branch back to :Ret and look for another START
    t Ret
   #pattern space didnt start w/ END, but is there even
   #one at all? if so replace it w/ a newline followed
   #by an E so we'll recognize it at the next :Kil
    s|END|\
E|
   #if that last was successful we'll have a newline
   #but if not it means we need to get the next line
   #if the last line we've got unmatched pairs and are
   #currently in a delete cycle anyway, but maybe we
   #should print up to our START marker in that case?
    /\n/!{
       #i guess so. now that i'm thinking about it
       #we'll swap into hold space, and Print it
        ${  x;P;d
        }
       #get next input line and add S after the delimiting
       #newline because we're still in START state. Delete
       #will handle everything up to our marker before we
       #branch back to :Kil at the top of the script
        N
        s||&S|
    }
   #now Delete will slice everything from head of pattern space
   #to the first occurring newline and loop back to top of script.
   #because we've definitely made successful substitutions if we
   #have a newline at all we'll test true and branch to :Kil 
   #to go again until we've definitely got ^E
    D

...コメントを省く...

#!/usr/bin/sed -f
    t Kil
    :Ret
    /START/!b
    s||\
S|
    h;D
    :Kil
    /^E/{
        H;x
        s|\nS.*\nE||
    }
    t Ret
    s|END|\
E|
    /\n/!{
        ${  x;P;d
        }
        N
        s||&S|
    }
    D

コメント付きバージョンをクリップボードにコピーして、次の操作を実行しました。

{ xsel; echo; } >se.sed
chmod +x se.sed
./se.sed <se.sed

#!/usr/bin/sed -f
####replace everything between
   #branch to :Kil if a successful substitution
   #has already occurred. this can only happen
   #if pattern space has been Deleted earlier
    t Kil
   #set a Ret :label so we can come back here
   #when we've cleared a  occurrence
   #and check for another if need be
    :Ret
   #if no  at the head of
   #pattern space.
    :Kil
   #do we?
    /^E/{
       #if so, we'll append it to our earlier save
       #and slice out everything between the two newlines
       #we've managed to insert at just the right points
        H;x
        s|\nS.*\nE||
    }
   #if we did just clear  we should
   #branch back to :Ret and look for another , but is there even
   #one at all? if so replace it w/ a newline followed
   #by an E so we'll recognize it at the next :Kil
    s|END|\
E|
   #if that last was successful we'll have a newline
   #but if not it means we need to get the next line
   #if the last line we've got unmatched pairs and are
   #currently in a delete cycle anyway, but maybe we
   #should print up to our

答え3

ファイルが test.txt の場合は、次を使用できます。

sed ':a;N;$!ba;s/\n/ /g' test.txt|sed 's/\\{{\[}.*{\]}\\}//' 

最初の sed はすべての改行を削除し、2 番目の sed はタグ内のテキストを削除します。

もっと一般的な解決策が必要かどうかは分かりません

関連情報