テキストファイルで繰り返されるブロック内で行を置き換える方法(sed / awkを使用する?)

テキストファイルで繰り返されるブロック内で行を置き換える方法(sed / awkを使用する?)

次のファイルがあります。

x = {
   y = {
       z = {
           block = {
              line1
              line2
              line3
           }
        }
    }
}
x2 = {
     y2 = {
         block = {
              line4
              line5
         }
     }
}
xyz
block = {
      line6
}

など。ブロック内の行を反転する必要がありますが、他のすべての項目は次のように順番に保持する必要があります。

x = {
   y = {
       z = {
           block = {
              line3
              line2
              line1
           }
        }
    }
}
x2 = {
     y2 = {
         block = {
              line5
              line4
              
         }
     }
}
xyz
block = {
      line6
}

sedまたはawkを使用してこれを行うには?ブロックを逆さまに反転しましたが、再び所定の位置に置くことはできません。

sed -n '/block = {.$/,/}$/p' inputfile | tac

答え1

Perlを使っても大丈夫なら:

perl -ne 'if($f && /}/){$f=0; print @blk};
          $f ? unshift(@blk, $_) : print;
          if(/block = {/){$f=1; @blk=()}' ip.txt
  • if(/block = {/){$f=1; @blk=()}配列を初期化し、入力行に以下が含まれている場合に@blkフラグを設定します。$fblock = {
  • $f ? unshift(@blk, $_) : printこのフラグが有効な場合は配列の前に入力行を挿入し@blk、それ以外の場合は入力行を印刷します。
  • if($f && /}/){$f=0; print @blk}フラグが有効になっていて入力行に以下が含まれている場合は、フラグを設定解除して配列}の内容を印刷します。@blk

そしてawk

awk 'f && /}/{f=0; for(i=c; i>=1; i--) print blk[i]}
     {if(f) blk[++c] = $0; else print}
     /block = {/{f=1; c=0}' ip.txt

答え2

インデントを使用してその端をperl見つけるもう1つの方法は次のとおりです。}block = {

perl -C -0777 -pe '
  s{^(\h*)block = \{\n\K.*?\n(?=\1\}$)}{
    join "", reverse $& =~ m{.*\n}g
  }gems' < your-file

答え3

sedコードを書くのは簡単ですが、読みにくいことを証明してください。

sed '/block = {/,/}/{/[{}]/!{G;h;d;};x;s/.$//p;g;s/.*//;x;}' file

予約済みスペースに現在予約済みスペースを追加して、反転する行(中括弧のない行)を収集します(それで順序は自動的に変更されます)。コードは移植可能であり、sedどの実装でも使用できます。詳細:

  • /block = {/,/}/block = {}自分で選択したように、次の範囲を選択してください。他のすべての同様のコードはxyz変更されずに印刷されます。
  • /[{}]/!{G;h;d;}中かっこなしの行処理(ブロック行):G現在予約されているスペースを追加し、h結果を予約済みスペースに移動し、出力とともにそのd行の処理を停止します。
  • 残りはサポートラインに対してのみ実行されます。ここでxバッファを変更して予約済みスペースの行を印刷し、予約済み空きs/.$//pスペースに追加して導入された予約済みスペースから末尾の改行を削除する必要があります。 ubstitudeコマンドpのオプションは、空のs予約スペースが印刷されるのを防ぐために、正常な置換にのみ印刷されます。最後にg予約されたスペースから現在の行を復元するために使用されます。
  • 最後に、保持スペースを空にする必要があります。s/.*//;x:現在の行をh前のスペースに保存し、パターンスペースを空にしてバッファを交換することで、保持スペースは空になり、パターンスペースには出力用の現在の行が再び含まれます。

答え4

使用幸せ(以前のPerl_6)

~$ raku -ne 'BEGIN my @blk; if (/block/ && .put) { for lines() {
             if /\}/  { put join "\n", (@blk.pop xx @blk.elems, $_); last};  
             @blk.push: $_ }
             } else { .put };'   file

#OR (more simply)

~$ raku -ne 'BEGIN my @blk; if (/block/ & .put) { for lines() { 
             if /\}/ { .put for (@blk.pop xx @blk.elems, $_).flat; last};  
             @blk.push: $_ }
             };'  file

上記のコードはPerlファミリーの言語であるRakuで書かれています。-ne非自動印刷フラグを使用して入力を1行ずつ読みます。BEGINブロック内で@blk配列が宣言され、if「ブロック」文字列(キーワード)が見つかり、lines最後}の閉じ括弧が読み込まれます。読んで配列でlines編集し、Curlieの終わりが見つかったら完全に(つまり、逆の順序で)閉じます。push@blkpop}last

入力例:

x = {
   y = {
       z = {
           block = {
              line1
              line2
              line3
           }
        }
    }
}
x2 = {
     y2 = {
         block = {
              line4
              line5
         }
     }
}
xyz
block = {
      line6
}

出力例:

x = {
   y = {
       z = {
           block = {
              line3
              line2
              line1
           }
        }
    }
}
x2 = {
     y2 = {
         block = {
              line5
              line4
         }
     }
}
xyz
block = {
      line6
}

付録:@Sundeepの優れたPerl5ソリューションのRaku翻訳を探している人は、以下を参照してください。

~$ raku -ne 'BEGIN my @blk; state $f; 
             if ($f && /\}/) {$f=0; .put for @blk}; 
             $f ?? unshift(@blk, $_) !! .put; 
             if (/block/) {$f=1; @blk=()};'  file

https://raku.org

関連情報