sed 剥离内联注释

sed 剥离内联注释

我有一个简单的 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的维护者。

相关内容