如何使用文件(用#定义)中的sed删除所有注释,而不是字符串中的'#'?
This帮助了很多,除了字符串部分。
答案 0 :(得分:10)
如果#
总是意味着评论,并且可以出现在一行的任何地方(例如在某些代码之后):
sed 's:#.*$::g' <file-name>
如果您想要更改它,请添加-i
开关:
sed -i 's:#.*$::g' <file-name>
这将从任何#
删除到行尾,忽略任何上下文。如果你在任何不是评论的地方使用#
(比如在字符串中),它也会删除它。
如果评论只能从一行的开头开始,请执行以下操作:
sed 's:^#.*$::g' <file-name>
如果它们之前可能有空格,但没有别的,请执行:
sed 's:^\s*#.*$::g' <file-name>
这两个会更安全一些,因为它们可能不会删除代码中#
的有效用法,例如字符串。
修改强>
没有一种很好的方法来检测字符串中是否存在某些内容。如果能满足你语言的限制,我会使用最后两个。
检测您是否在字符串中的问题是正则表达式无法执行所有操作。有一些问题:
正则表达式不能匹配嵌套引号(这些情况会混淆正则表达式):
# "hello there"
# hello there"
"# hello there"
如果双引号是定义字符串的唯一方法,双引号将永远不会出现在注释中,并且字符串不能跨越多行,请尝试以下操作:
sed 's:#[^"]*$::g' <file-name>
这是很多先决条件,但如果他们都持有,那么你就是在做生意。否则,我担心你是SOL,你最好用Python之类的东西编写它,在那里你可以做更高级的逻辑。
答案 1 :(得分:5)
这可能适合你(GNU sed):
sed '/#/!b;s/^/\n/;ta;:a;s/\n$//;t;s/\n\(\("[^"]*"\)\|\('\''[^'\'']*'\''\)\)/\1\n/;ta;s/\n\([^#]\)/\1\n/;ta;s/\n.*//' file
/#/!b
如果该行不包含#
保释s/^/\n/
插入一个唯一标记(\n
)ta;:a
跳转到循环标签(重置替换的true / false标志)s/\n$//;t
如果标记位于该行的末尾,请删除并拯救s/\n\(\("[^"]*"\)\|\('\''[^'\'']*'\''\)\)/\1\n/;ta
如果标记后面的字符串是引号,则将标记向前撞击并循环。s/\n\([^#]\)/\1\n/;ta
如果标记后面的字符不是#
,请将标记向前撞击并循环。s/\n.*//
该行的其余部分是注释,删除标记和行的其余部分。答案 2 :(得分:3)
由于提问者没有提供样本输入,我将假设几个案例,而Bash是输入文件,因为bash用作问题的标记。
案例1 :整行是评论
在大多数情况下,以下内容应足够了:
sed '/^\s*#/d' file
它匹配任何没有或至少有一个前导空格字符的行(空格,制表符或其他几个,请参阅man isspace
),然后是#
,然后删除d
命令行。
任何行如:
# comment started from beginning.
# any number of white-space character before
# or 'quote' in "here"
它们将被删除。
但是
a="foobar in #comment"
不会被删除,这是期望的结果。
案例2 :实际代码后的评论
例如:
if [[ $foo == "#bar" ]]; then # comment here
评论部分可以通过
删除sed "s/\s*#*[^\"']*$//" file
[^\"']
用于防止引用字符串混淆,但是,这也意味着不会删除带引号'
或"
的评论。
最终sed
sed "/^\s*#/d;s/\s*#[^\"']*$//" file
答案 3 :(得分:2)
删除注释行(第一个非空白字符为#
)的行,但不 shebang行(第一个字符为#!
的行):
sed '/^[[:space:]]*#[^!]/d; /#$/d' file
sed
的第一个参数是一个包含sed程序的字符串,该程序包含两个/
正则表达式 /d
形式的删除行命令。命令由;
分隔。第一个命令删除注释行但不删除shebang行。第二个命令删除任何剩余的空注释行。它不处理尾随注释。
sed
的最后一个参数是一个用作输入的文件。在Bash中,您还可以对字符串变量进行操作:
sed '/^[[:space:]]*#[^!]/d; /#$/d' <<< "${MYSTRING}"
示例:
# test.sh
S0=$(cat << HERE
#!/usr/bin/env bash
# comment
# indented comment
echo 'FOO' # trailing comment
# last line is an empty, indented comment
#
HERE
)
printf "\nBEFORE removal:\n\n${S0}\n\n"
S1=$(sed '/^[[:space:]]*#[^!]/d; /#$/d' <<< "${S0}")
printf "\nAFTER removal:\n\n${S1}\n\n"
输出:
$ bash test.sh
BEFORE removal:
#!/usr/bin/env bash
# comment
# indented comment
echo 'FOO' # trailing comment
# last line is an empty, indented comment
#
AFTER removal:
#!/usr/bin/env bash
echo 'FOO' # trailing comment
答案 4 :(得分:1)
假设“在一个字符串中”意味着“发生在一对引号之间,无论是单引号还是双引号”,这个问题可以改为“在第一个未引用的#之后删除所有引号”。反过来,您可以将引用的字符串定义为两个引号之间的任何内容,但反向引号除外。作为一个小改进,在第一个没有引用的#。
之前,用一切代替整条线所以我们得到类似于[^\"'#]
的简单案例 - 一段字符串既不是注释符号,也不是反斜杠,也不是开头引用。然后我们可以接受反斜杠后跟任何东西:\\.
- 这不是文字点,这是一个字面反斜杠,后跟一个匹配任何字符的点元字符。
然后我们可以允许零个或多个重复引用的字符串。为了接受单引号或双引号,允许每个引号中的零个或多个。引用的字符串应定义为开头引号,后跟零或更多的反向任意字符,或除结束引号之外的任何字符:"\(\\.\|[^\"]\)*"
或类似的单引号字符串'\(\\.\|[^\']\)*'
。< / p>
将所有这些拼凑在一起,您的sed
脚本可能如下所示:
s/^\([^\"'#]*\|\\.\|"\(\\.\|[^\"]\)*"\|'\(\\.\|[^\']\)*'\)*\)#.*/\1/
但是因为它需要被引用,并且单引号和双引号都包含在字符串中,我们还需要一个额外的复杂功能。回想一下,shell允许你将"foo"'bar'
字符串粘合在一起,用双引号替换为foobar
- foo
,用单引号括住bar
。因此,您可以通过将单引号括在单引号字符串旁边的双引号中来包含单引号 - '"foo"'"'"
"foo"
在'
旁边的单引号中以双引号括起来,因此{{1 }};并且"foo"'
可以表示为与"'
相邻的'"'
。因此,包含双引号"'"
的单引号字符串可以与foo"'bar
附近的'foo"'
引用,或者对于"'bar"
与'foo"'
相邻的情况更为逼真}}与另一个单引号字符串"'"
相邻,产生'bar'
。
'foo'"'"'bar'
这是在Linux上测试过的;在其他平台上,sed 's/^\(\(\\.\|[^\#"'"'"']*\|"\(\\.\|[^\"]\)*"\|'"'"'\(\\.\|[^\'"'"']\)*'"'"'\)*\)#.*/\1/p' file
方言可能略有不同。例如,您可能需要在分组和更改运算符之前省略反斜杠。
唉,如果你有多行引用的字符串,这将不起作用; sed
按设计,一次只检查一个输入行。您可以构建一个复杂的脚本,将多行收集到内存中,但到那时,切换到例如Perl开始变得很有意义。
答案 5 :(得分:0)
正如您所指出的,如果脚本的任何部分看起来像评论但实际上不是,那么sed将无法正常工作。例如,您可以在字符串中找到#,或者相当常见的$#
和${#param}
。
我编写了一个名为shfmt的shell格式化程序,它具有缩小代码的功能。这包括删除评论,以及其他内容:
$ cat foo.sh
echo $# # inline comment
# lone comment
echo '# this is not a comment'
[mvdan@carbon:12] [0] [/home/mvdan]
$ shfmt -mn foo.sh
echo $#
echo '# this is not a comment'
解析器和打印机是Go包,所以如果你想要一个自定义解决方案,编写一个20行的Go程序就可以很容易地以你想要的方式删除注释。
答案 6 :(得分:-1)
sed 's:^#\(.*\)$:\1:g' filename
假设行以单#注释开始,Above命令从文件中删除所有注释。