我有一个简单的 bash 脚本来从 js 文件中删除注释:
#!/bin/bash
sed -E '/^[[:blank:]]*(\/\/|#)/d;s/#.*//' $1 >> stripped.js
除了内嵌的注释之外,这几乎是完美的,例如
// file-to-be-stripped.js
...
...
const someVar = 'var' // this comment won't be stripped
// this comment will be stripped
我缺少什么来删除内联评论?
更新:
真正奇怪的是我用在线 bash shell 启动了你的示例,它运行得完美无缺!但是,当我在本地运行完全相同的代码时,它不会删除内联代码!?知道为什么/怎么会这样吗?我显然错过了一些东西......非常奇怪。
这是我更新的代码:
我的脚本:stripper.sh
#!/bin/bash
sed -E -e 's:(\s+(//|#)|^\s*(//|#)).*$::; /^$/d' $1 > "stripped.${1}"
我的测试文件:test.js
// testies one
const testies = 'two'
console.log(testies) // three
// testies FOUR!?
console.log('Mmmmm toast') // I won't be stripped of my rights!
然后我执行:./stripper.sh test.js
输出是:
const testies = 'two'
console.log(testies) // three
console.log('Mmmmm toast') // I won't be stripped of my rights!
任何想法为什么只在本地运行完全相同的代码 sed 的整行注释却用在线 bash 解释器(不幸的是,我无法分享到我的 shell 的确切链接,因为它是一个 bit.ly 链接,显然这里是“不”。)是否按预期工作?
答案1
POSIXly,你会这样做:
sed '
s|[[:blank:]]*//.*||; # remove //comments
s|[[:blank:]]*#.*||; # remove #comments
t prune
b
:prune
/./!d; # remove empty lines, but only those that
# become empty as a result of comment stripping'
使用 GNUsed
我们可以将其缩短为:
sed -E 's@[[:blank:]]*(//|#).*@@;T;/./!d'
请注意,它会很乐意删除#things
并且//things
不是像以下这样的评论:
const url = 'http://stackexchange.com';
x = "foo#bar";
要忽略#
,//
内引号,您可以这样做:
perl -ne 'if (/./) {
s{\s*(?://|#).*|("(?:\\.|[^"])*"|'"'(?:\\\\.|[^'])*'"'|.)}{$1}g;
print if /./} else {print}'
在这样的输入上:
#blah
// testies one
const testies = 'two';
console.log(testies) // three
const url = 'http://stackexchange.com';
x = "not#a comment";
y = "foo\"bar" # comment
y = 'foo\'bar' # it's a comment
它给:
const testies = 'two';
console.log(testies)
const url = 'http://stackexchange.com';
x = "not#a comment";
y = "foo\"bar"
y = 'foo\'bar'
(您可能需要适应这些文件的实际语言(我不知道 JavaScript 支持#
注释,除了以#!
node.js 开头的第一行))。
答案2
sed -e '/^\/\//d' -e 's@\(.*\)[[:blank:]]\{1,\}//.*@\1@' your_file
此 sed 命令删除以注释开头的行,对于内联注释,它会删除分隔代码从注释到行尾的空格中的所有内容。它是 POSIX(未使用 GNU 扩展),并且根据 OP 的原始示例并为了便于阅读,此版本仅支持//
注释(更多内容见下文)。
细节
此sed
调用包括两个 sed 命令:“模式匹配时删除”和替换。
前者是/^\/\//d
.该模式^\/\/
匹配以两个斜杠开头的行(例如“// foo bar”)。这些行被删除并立即引入下一行(即跳过替换)。
替换中的模式是\(.*\)[[:blank:]]\{1,\}//.*
。注意:我使用@
作为分隔符是为了避免/
分隔符需要的某些字符转义。
\(
..\)
- 任何匹配的内容都可以作为后向参考.*
- 匹配 0 个或多个字符(除了换行符之外的任何字符);在替换部分中,由于周围的\(
和,我们可以引用此处匹配的任何内容\)
。[[:blank:]]
- 一个空白字符\{1,\}
- 匹配它前面的一个或多个内容([[:blank:]]
在本例中)//
- 匹配两个斜杠(即评论的开头).*
- 与上面相同,但不可用作反向参考
替换部分只是\1
表示替换我们与第一个反向引用匹配的任何内容,即.*
前面的[[:blank:]]
。
所以它的工作原理就像我所描述的那样:对于内联注释,删除将代码从注释分隔到行尾的空白中的所有内容。
'#' 评论
使用 GNU sed 添加注释处理#
只是//
用替换替换(#|//)
(或者如果我们需要转义\(#\|\/\/\)
)的问题。然而,以 POSIX 方式执行此操作要冗长得多,因为不支持交替。显然,您可以通过重复现有的 sed 命令以及#
.更好的是,已经发布了一个答案,显示了一种更简洁的方法。不管怎样,我不会在这里重复解决方案。
编辑:
经过很长时间后重新审视这一点,我意识到替换比它需要的更复杂,并且正如评论中指出的那样,除了(例如“something // foo // bar”..only “//栏”被删除)。
我相信这就是我们所需要的......
sed -e '/^\/\//d' -e 's@[[:blank:]]\{1,\}//.*@@' your_file
也就是说,替换部分表示“在我们遇到的第一个空格-斜杠-斜杠处,删除它以及后面的所有内容,同时保留任何前面的文本”。
答案3
使用GNU sed
我们可以编写一个小型解析器代码来过滤C++
样式注释,//
以及sh
样式注释#
。
为了使结构模块化且可扩展,我们使用在 shell 变量中定义并适当引用的固定正则表达式。
代码sed
让我们传递空行。然后它会寻找行中不平衡的双引号。它不断抓住下一行,直到它们变得平衡。这是为了使引号溢出到多行。
单引号也是如此。
接下来,我们寻找任何连续行,通过尾部反斜杠标识。
最后,我们不断跳过不是评论的引用词或裸词。
如果经过此转换后我们什么都没有留下,那么我们将其删除,OTW 我们立即将该decommentified
行打印到标准输出。
PS:我们在 sed -e ... 中混合使用单引号和双引号来解决bash
命令行上的错误,其中!
双引号内的字符是不可抑制的,因此我们将其放在单引号中。
# symbol names
q=\' Q=\"
d=\$ b=\\
B=$b$b
# construct regexes using symbolic names
single_quotes_open="$q[^$b$q]*($B.[^$b$q]*)*$d"
single_quoted_word="$q[^$b$q]*($B.[^$b$q]*)*$q"
double_quoted_word="$Q[^$b$Q]*($B.[^$b$Q]*)*$Q"
double_quotes_open="$Q[^$b$Q]*($B.[^$b$Q]*)*$d"
quoted_word="$double_quoted_word|$single_quoted_word"
# decomment a c++ file
sed -Ee '
/\S/!b'"
:a;/(^|\s)$double_quotes_open/{N;ba;}
:b;/(^|\s)$single_quotes_open/{N;bb;}
:c;/$B$d/{N;bc;}
s_\s*(//|#).*|($quoted_word|.)_\2_g
"'/\S/!d
' c_file
答案4
如果您想从源文件中删除注释,您可以尝试我的comcat
工具。最新的一晚版本可用在 GitHub 上。
- 它可以只显示评论,也可以显示除评论之外的所有内容。
- 这是一个非常年轻的项目,所以预计会出现一些错误。
我确实意识到这是一个关于 的问题sed
。如果您认为此答案没有用,可以将其删除。
免责声明: 我是comcat的维护者。