3

いくつかのパターン間でのみいくつかの置換を行いたいとしましょう。それら<a></a>明確にするために...(大丈夫大丈夫、彼らはそうです!.. Jeez!startend

したがって、同じ行startで常に発生する場合の対処方法を知っています。適切な正規表現を設計するだけです。end

また、それらが異なる行にあることが保証されている場合の対処方法も知っており、を含む行に何も気にせず、endを含む行にすべてのコマンドを適用しても問題ありません。アドレス範囲を指定するだけです。として。start start/start/,/end/

ただし、これはあまり便利ではありません。{...}たとえば、ブロック内に変更を導入するなど、よりスマートな仕事をする必要がある場合はどうなりますか?

私が考えることができることの1つは、処理前と処理前に入力を分割し、{後で}元に戻すことです。

sed 's/{\|}/\n/g' input | sed 'main stuff' | sed ':a $!{N;ba}; s/\n\(}\|{\)\n/\1/g'

別のオプションは反対です:

cat input | tr '\n' '#' | sed 'whatever; s/#/\n/g'

これらは両方とも醜いです。これは主に、操作が1つのコマンドに限定されていないためです。2つ目は、元のテキストに存在しないと仮定して、「改行ホルダー」として文字またはサブストリングを使用する必要があるため、さらに悪化します。

したがって、問題は次のとおりです。より良い方法はありますか、それとも上記の方法を最適化できますか?これは、最近のSOの質問で読んだものからはかなり定期的なタスクなので、ベストプラクティスを一度だけ選択したいと思います。

PS私は主に純粋なsedソリューションに興味があります:仕事は1回の呼び出しだけで行うことができsedますか?awkいいえ、などをお願いPerlします。これは理論的な質問であり、「できるだけ早く仕事をする必要がある」という質問ではありません。

4

1 に答える 1

3

これはあなたのために働くかもしれません:

# create multiline test data
cat <<\! >/tmp/a
> this
> this { this needs
> changing to
> that } that
> that
> !
sed '/{/!b;:a;/}/!{$q;N;ba};h;s/[^{]*{//;s/}.*//;s/this\|that/\U&/g;x;G;s/{[^}]*}\([^\n]*\)\n\(.*\)/{\2}\1/' /tmp/a
this
this { THIS needs
changing to
THAT } that
that
# convert multiline test data to a single line
tr '\n' ' ' </tmp/a >/tmp/b
sed '/{/!b;:a;/}/!{$q;N;ba};h;s/[^{]*{//;s/}.*//;s/this\|that/\U&/g;x;G;s/{[^}]*}\([^\n]*\)\n\(.*\)/{\2}\1/' /tmp/b
this this { THIS needs changing to THAT } that that

説明:

  • データをパターン空間(PS)に読み込みます。/{/!b;:a;/}/!{$q;N;ba}
  • データをホールドスペース(HS)にコピーします。h
  • 文字列の前後から非データを削除します。s/[^{]*{//;s/}.*//
  • データを変換します。s/this\|that/\U&/g
  • HSにスワップし、変換されたデータを追加します。x;G
  • 古いデータを変換されたデータに置き換えます。s/{[^}]*}\([^\n]*\)\n\(.*\)/{\2}\1/

編集:

より複雑な答えは、1行に複数のブロックに対応していると思います。

# slurp file into pattern space (PS)
:a
$! {
N
ba
}
# check for presence of \v if so quit with exit value 1
/\v/q1
# replace original newlines with \v's
y/\n/\v/
# append a newline to PS as a delimiter
G
# copy PS to hold space (HS)
h
# starting from right to left delete everything but blocks
:b
s/\(.*\)\({.*}\).*\n/\1\n\2/
tb
# delete any non-block details form the start of the file
s/.*\n//
# PS contains only block details
# do any block processing here e.g. uppercase this and that
s/th\(is\|at\)/\U&/g
# append ps to hs
H
# swap to HS
x
# replace each original block with its processed one from right to left
:c
s/\(.*\){.*}\(.*\)\n\n\(.*\)\({.*}\)/\1\n\n\4\2\3/
tc
# delete newlines
s/\n//g
# restore original newlines
y/\v/\n/
# done!

注意:これはGNU固有のオプションを使用しますが、一般的なsedで動作するように調整できます。

于 2012-06-15T09:36:31.080 に答える