如何使用 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

我可以用awkor来实现这一点sed吗?我可以通过再次“评论”的操作轻松反转它吗?

我想避免的是犯错误,所以如果这些行已经被注释掉了,就应该保留它们。另外,在“comment in”中,如果 end 和 begin 之间的行不以 开头,则不应尝试执行某些操作#


找到了一个可能的解决方案:

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'

我还编写了一个附带的小 shell 脚本:

#!/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您用于检测和行的模式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

在这两个命令中,我使用字符串比较而不是正则表达式,因为它们通常更快,并且不需要涉及任何模式匹配。第二个命令中的调用substr()(用于取消注释)将返回输入字符串减去第一个字符。单独的尾随1导致当前行被输出。

相关内容