我正在尝试使用正则表达式创建一个 sed 命令,以便仅在文本文件中没有注释的情况下替换它,但由于我对 sed 命令几乎不了解,我遇到了一些麻烦。
我找到了问题的一小部分的解决方案,但有些还不够完整,或者我无法将它们放在一起。TL;DR 版本在最后可用。
首先我们要确定我的最终目标
我想匹配文本文件中的任何内容(例如任何常规正则表达式(呵呵))不是评论道。由于我想针对多种语言进行此操作,因此我们只采用常见的 C 注释。
因此,在这种情况下,可以用不同的方式注释单词或行。我们只能//
评论接下来的内容,并且我们还有/* */
评论块。
环境
我目前正在使用 Mac OSX,它只支持 POSIX sed,但我安装了一个我发现更好的 GNU-sed。 (感谢Homebrew
。包是gnu-sed
,命令是gsed
。)因此,如果您更喜欢使用其中之一,我都可以使用它们。
我在假设使用 GNU-sed 的情况下写这篇文章。
忽略一个案例
第一个问题,如何忽略某些情况。我发现很容易在这个话题。
现在,这//
部分对我来说似乎很容易做到,我只需要添加一个或者 (|
)条件将其与其他条件结合起来。
它看起来像这样:
sed -E "/\/\/.*/! s/foo/bar/" file
那么,如果输入文件是:
foo
42
test
//foo
//42
// foo
//something foo
foo
42
something foo
foo
输出是:
bar
42
test
//foo
//42
// foo
//something foo
bar
42
something bar
bar
所以现在,我只想把我的反思集中在/* */
评论区。
通过多行匹配
第二个问题,如何使正则表达式通过多行进行匹配。嗯,我认为这是主要问题。我发现这个话题讨论如何仅通过一个换行符进行匹配。嗯,我花了一点时间才明白它是如何工作的。但这部分解决方案给我带来了新的问题和新的疑问。
显然它只能忽略一个新行( \n
)。所以我现在想要做同样的事情,但是对于未知数量的行(从 0 到无限 ( *
))。我打赌我必须循环遍历这些行,但这就是我陷入困境的地方,因为我对 sed 的命令一无所知,这对我来说真的很尴尬。
在我的搜索过程中,我发现了一个小脚本目的是替换tail
命令,它使用循环(我猜),但我无法理解它的功能。
使其仅在该*/
部分之前匹配
第三部分是确保忽略的大小写仅匹配注释块结束之前的内容 ( */
)。因此,最终,忽略大小写只会匹配/*
和之间的内容*/
。最终的命令将完全忽略注释块中写入的内容。
我没有对这部分进行真正的搜索,因为我没有解决前一点,在我看来,这个*/
问题取决于/*
前一个问题。
最后一部分:将所有这些放在一起
嗯,很明显我此刻完全失败了。
长话短说
我的问题是: sed 命令是什么,以便仅在文本文件中没有注释的情况下替换我们想要的任何内容?
附录
如果您知道使用任何其他语言更简单的方法,也非常受欢迎。因此,如果您知道如何使用 或其他任何东西来做到这一点awk
,python
请随时分享。
答案1
如果他们告诉你这是不可能的,你不应该相信他们。你应该然而,相信他们,如果他们告诉你这并不容易。
sed '\|*/|!{ s|/\*|\n&| #if ! */ repl 1st /* w/ \n/*
h; s|foo|bar|g;/\n/!b #hold; repl all foo/bar; if ! \n branch
G; s|\n.*\n||;:n #Get; clear difference; :new label
n; \|*/|!bn;s|^|\n/*| #new line; if ! */ branch new label
};s|*/|\n&|g #repl all */ w/ \n*/
s|foo|&\nbar|g;:r #repl all foo w/ foo\nbar
s|\(/\*[^\n]*\)\nbar|\1|g;tr #repl all /*[^\n]*\nbar w/ foo
s|foo\n\(b\)|\1|g #repl all foo\nbar w/ bar
s|^\n/.||;s|\n||g #clear any \n inserts
' <<\INPUT
asfoo /* asdfooasdfoo
asdfasdfoo
asdfasdfoo
foo */foo /*foo*/ foo
/*.
foo*/
foo
hello
INPUT
输出
asbar /* asdfooasdfoo
asdfasdfoo
asdfasdfoo
foo */bar /*foo*/ bar
/*.
foo*/
bar
hello