sedまたはawkを使用してソースコードの特定の部分をコメントアウトまたはコメントアウトする方法

sedまたはawkを使用してソースコードの特定の部分をコメントアウトまたはコメントアウトする方法

現在、私のファイルには次のセクションが含まれています。

code statement1
code statement2
# BEGIN SOMENAME
some code
some other code
# END SOMENAME
code statement n +1
code statement n +1

私がしたいのは、間の内容をコメントアウトすることです。

# BEGIN SOMENAME

そして

# END

だから結局、次のように見えます。

code statement1
code statement2
# BEGIN SOMENAME
# some code
# some other code
# END SOMENAME
code statement n +1
code statement n +1

awkまたはを使用してこれを達成できますかsed?もう一度「コメントする」だけで簡単に元に戻せますか?

私が避けたいのは間違いです。したがって、この行がすでにコメント化されている場合はそのままにしてください。また、「コメント入力」でendとstartの間の行がで始まらない限り、何かを試してはいけません#


考えられる解決策が見つかりました。

awk '
    BEGIN { 
        i=0; 
        line_with_no_comment_found=0 
    }
    
    /^# END/ { 
        m=0;
        if ( line_with_no_comment_found == 1 ) { 
            for (var in a) print "# "a[var] 
        } else { 
            for (var in a) print a[var] 
        }
        delete a;
        i=0;
        line_with_no_comment_found=0;
    }
    
    /^# / { 
        if (m==0) { 
            print 
        } else { 
            a[i++]=$0; 
        }
    }
    
    !/^# / { 
        if (m==0) { 
            print 
        } else { 
            a[i++]=$0; 
            line_with_no_comment_found=1
        }
    }
    
    /^# BEGIN ([a-zA-Z_])([1-9][0-9]*)*/ {
        m=1;
    }
    END { }
'<<EOF

答え1

このスクリプトは私にとって効果的です。私はこれをGNU Awk 4.0.1でテストしましたが、Nawkでも動作します。

awk 'BEGIN {
    # action=0: uncomment
    # action=1: comment
    action=0
    in_optional_code_block=0
}
{
    if ($0 ~ /^# BEGIN/) {
        in_optional_code_block=1
    } else if ($0 ~ /^# END/) {
        in_optional_code_block=0
    } else if (in_optional_code_block) {
        if (action) {
            if ($0 !~ /^#/) {
                $0 = "# " $0
            }
        } else {
            if ($0 ~ /^#/) {
                sub(/^# ?/, "")
            }
        }
    }
}
1'

私もそれに付属の小さなシェルスクリプトを書いています:

#!/usr/bin/env sh

syntax_error() {
    echo "Usage: `basename \"$0\"` [comment|uncomment] file" >&2
    exit 1
}

case "$1" in
    0|uncomment) action=0; ;;
    1|comment) action=1; ;;
    *) syntax_error; ;;
esac
shift
if [ -z "$@" ]; then syntax_error; fi

awk 'BEGIN {
    action='$action'
    in_optional_code_block=0
}
{
    if ($0 ~ /^# BEGIN/) {
        in_optional_code_block=1
    } else if ($0 ~ /^# END/) {
        in_optional_code_block=0
    } else if (in_optional_code_block) {
        if (action) {
            if ($0 !~ /^#/) {
                $0 = "# " $0
            }
        } else {
            if ($0 ~ /^#/) {
                sub(/^# ?/, "")
            }
        }
    }
}
1' "$@" > "[email protected]"
if [ $? -eq 0 ]; then mv "[email protected]" "$@"; fi

(GNU Awk 4.1.0以降では、構造体を移動するのではなく、最後に-iフラグを使用できます。)

答え2

質問の「可能な解決策」を解決すると、提案されたコードにはいくつかの問題があります。

  • ループはfor (var in a)インデックスを順番に繰り返すことは保証されていないため、コメントアウトされた行はaランダムな順序で潜在的に出力される可能性があります。
  • BEGIN合計行を検出するために使用するパターンは、2行ENDで使用されているラベルをペアで連結しようとしないため、で終わることがあり# BEGIN FOOます# END BAR
  • /^# /ブロック間に不要なコードの重複があります!/^# /
  • 空のブロックがEND不必要に存在します。
  • コードのコメントは解除されませんでした。

注釈の影響を受けるセクションで、コメントアウトされた行のコメントアウトを簡単に無効にするために、開くと閉じるバー#に追加の文字も追加しました。

sed '/^# BEGIN SOMENAME$/,/^# END SOMENAME$/ s/^/#/' file

これにより、#対応するバーを含む行と行の間のすべての行の先頭に文字が挿入されます。# BEGIN SOMENAME# END SOMENAME

特定のテキストに対して、次の結果が発生します。

code statement1
code statement2
## BEGIN SOMENAME
#some code
#some other code
## END SOMENAME
code statement n +1
code statement n +1

その文字がまだ最初の文字ではない行にのみ挿入されている場合、表示された行#の間に元の推奨行の記録がないため、反転操作は非常に困難です。

コメントを解除するには:

sed '/^## BEGIN SOMENAME$/,/^## END SOMENAME$/ s/#//' file

これは最初のコマンドで推奨される行にのみ影響し、各行sedの先頭に挿入された文字を削除して元の状態に戻します。#

#空白文字とそれに続く文字を使用して行をコメントアウトしたいと思います。

sed '/^# BEGIN SOMENAME$/,/^# END SOMENAME$/ s/^/# /' file

コメントを投稿するには

sed '/^# # BEGIN SOMENAME$/,/^# # END SOMENAME$/ s/# //' file

コメントをキャンセルするために使用されます。


awk代わりに同等の操作を使用してくださいsed

コメント:

awk '$0 == "# BEGIN SOMENAME", $0 == "# END SOMENAME" { $0 = "#" $0 }; 1' file

コメントをキャンセルするには:

awk '$0 == "## BEGIN SOMENAME", $0 == "## END SOMENAME" { $0 = substr($0, 2) }; 1' file

どちらのコマンドも正規表現の代わりに文字列比較を使用します。なぜなら、正規表現は通常より速く、パターンマッチングを必要としないからです。コメント解除のための2番目のコマンド呼び出しは、substr()入力文字列から最初の文字を引いた値を返します。後行だけで1現在行が出力されます。

関連情報