web-dev-qa-db-ja.com

sedまたはawkを使用して正規表現に一致する複数行をコメントアウトする方法

これを含むファイルがあります:

[...]

location /static {
    ...
    multiple lines
    ...
}

[...]

location /static/ {
    ...
    multiple lines
    ...
}

[...]

そして私は取得したい:

[...]

# location /static {
#     ...
#     multiple lines
#     ...
# }

[...]

# location /static/ {
#     ...
#     multiple lines
#     ...
# }

[...]

ファイルをunixコマンドに渡す方法を教えてください。

3
Natim

これは些細なことではありません。各{}ブロックに他のネストされた{}ブロックが含まれていないと想定できる場合は、より簡単で、次のようなことができます。

Perl -pe 'if(/location\s*\/static/){$n=1}elsif(/}/){$n=0} s/^/#/ if $n==1;' file

これは、現在の行が$nと一致する場合に1location /staticに設定し、0の後に見つかった最初の}location/staticに戻すだけです。 。次に、$n==1である限り、行の先頭に#を追加します。 -pフラグにより​​、Perlは入力ファイルを自動的にループし、各行を出力します。

ここで、コメントしたいブロック内に任意の深さのネストされたブロックを含めることができる場合、事態はさらに複雑になります。たとえば、次のようなものがある場合:

location /static {
   if(foo){
      print "one";
   }
   elsif(bar){
      print "two";
   }
}

そのような場合、上記の単純な解決策は失敗し、開いている{の数を追跡するものを使用する必要があります。たとえば(これは実際にはワンライナーです。端末に直接コピーして貼り付けることができます。わかりやすくするために拡張しただけです)。

Perl -pe 'if(/location\s*\/static/){$n=1;}
          elsif(/}/ && $open==0){$n=0} 
          if($n==1 && /{/){$open++} ## count open brackets
          elsif($n==1 && /}/){$open--} ## count closing brackets
          if($n==1 && $open>0){ s/^/#/}; ' file

最後に、ソリューションが期待どおりに機能する場合は、-iフラグを追加してファイル自体に変更を加えることができます。

Perl -i -pe 'if(/location\s*\/static/){$n=1}elsif(/}/){$n=0} s/^/#/ if $n==1;' file
2
terdon

正規表現を使用して(ネストされている可能性のある)区切られたブロックを抽出することは、特に楽しくも簡単でもありません。ただし、Perlに長い間出荷されているモジュールを使用する(Perlがタグの1つであるため)、洗練されたソリューションがあります。 Text :: Balanced

#!/usr/bin/env Perl
use strict;
use warnings;
use Text::Balanced qw( extract_bracketed );

my $in = do { local $/ = undef; <> };
while( $in ) {
    my $out;
    if    ( $in =~ s/^(location\s+\S+\s+)// ) { ( $out = $1 . extract_bracketed($in) ) =~ s/^/# /mg }
    elsif ( $in =~ s/^(.*[\r\n]*)// )         { $out = $1 }
    print $out;
}

このスクリプトは、文字列の先頭部分を繰り返し消費(抽出)して分析し、何も残らないようにすることで機能します。

  • 先頭の部分にlocationキーワードが含まれ、その後に空白(\s+)が続き、それが識別子である可能性があるように見える場合(現在、空白以外の文字のシーケンスであるため、非常に大雑把に識別されています) 、\S+)、次にextract_bracketedは、次の区切りブロックを抽出します(デフォルトでは、次のペアのいずれかで区切られたブロックを抽出します:[]{}()または<>)。 extract_bracketedは、抽出されるブロック内のネストされたバランスの取れた区切り文字を正しく処理します。次の置換s/^/# /mgは、ブロックに含まれる行数に関係なく、ブロック内の個々の行をコメントアウトする役割を果たします。次に、ブロック(先頭のlocationキーワードとともに)が出力されます。

  • それ以外の場合、行(改行文字まで)が抽出され、変更されずに印刷されます。

注意すべき他のいくつかの事柄:

  1. レコード区切り文字$inの定義を解除することにより、テキスト全体が読み取られ、文字列($/)に格納されます。
  2. $1は、括弧で区切られた正規表現の内容を保持する特別な変数です。たとえば、(location\s+\S+\s+)の場合、$ 1にはテキストlocation /static(末尾のスペースを含む)が含まれます。
0
Edward