目前,我的文件包含如下部分:
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
or来实现这一点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
导致当前行被输出。