【问题标题】:How to match )\n{ using sed?如何匹配 )\n{ 使用 sed?
【发布时间】:2019-06-10 16:43:36
【问题描述】:

我需要编写一个 shell 脚本,它会在我的 .c 文件中输入一个字符串,然后匹配一个相当复杂的模式。

模式为:)\n{

\n{ 之间没有任何制表符/空格。也就是说,我想与位于文件第一列中的 { 匹配(这是为了仅与 C 中的函数声明之后的 )\n{ 匹配,而不是循环或条件之后的那些)。

void some_function_declaration(char var1, char var2) {

我已经阅读了手册、论坛,但仍然无法找到正确的方法来编写模式以匹配或找到相应的正则表达式。相应的输出将是:

void some_function_declaration(char var1, char var2) { time_exe(__func__, cl(clock())); rest of the function...

以下是我到目前为止想出的但不起作用。

试试 1

sed -i '' '/)$\n{/a time_exe(__func__, cl(clock()));' >> list_func2.c

试试 2

sed -i '' '/)\n{ /a time_exe(__func__, cl(clock()));' >> list_func2.c

试试 3

sed -i '' '/)/\n{/a time_exe(__func__, cl(clock()));' >> list_func2.c

我很高兴听到您对此事的建议。

【问题讨论】:

  • 你不能用一个简单的 sed 正则表达式模式来做到这一点,因为 sed 是面向行的——文件是在换行符上分割的,所以,不做一些 sed 编程,你' 将永远不会在 sed 模式空间中看到换行符。而且,除非您愿意忍受,否则 sed 编程可能会很痛苦。

标签: bash shell sed glob metacharacters


【解决方案1】:

这可能对你有用(GNU sed):

sed ':a;/)$/!b;n;/^{/!ba;c{ time_exe(__func__, cl(clock()));' file

如果当前行不以) 结尾,则退出 sed 的任何进一步处理。否则,打印当前行并读入下一行。如果该行不是以 { 开头的,请从头开始检查,否则将其更改为所需的格式。

也可以附加或插入所需的格式,见下文:

sed ':a;/)$/!b;n;/^{/!ba;a{ time_exe(__func__, cl(clock()));' file

或者,

sed ':a;/)$/!b;n;s/^{/& time_exe(__func__, cl(clock()));/;Ta' file

【讨论】:

  • 感谢您的回答和精确的 cmets。我应该更明确地说明使用 sed 不是强制性的,因为我没有 GNU sed。我也尝试了您的答案,但并非在所有情况下都有效。有时会在我的函数声明后添加n...。希望这对其他人有所帮助。
  • @Aaron 你是对的,添加了对重做初始匹配的条件的检查。谢谢
  • @aboitier 在这些情况下,提供的解决方案不依赖于 GNU sed。当我提供答案时,我会使用我机器上的 sed,以避免潜在的误解(尤其是当答案由于事后的想法而必须改进时)我用GNU sed 记录它。
【解决方案2】:

你应该避免使用sed,它是一个基于行的工具,不能很好地处理这种任务。

如果您坚持使用sed 并且正在使用GNU sed,则可以使用-z / --null-data 选项,该选项将一次性读取整个文件(读取NUL 字节分隔的记录而不是换行-separated records) 并且使您能够按照您的期望使用)\n{ 模式:

$ { echo "line1)"; echo "{line2"; } | sed -z 's/)\n{/X/g'
line1Xline2

由于这需要将整个文件加载到内存中,因此对于大文件来说,性能会很糟糕。


如果你喜欢不可维护的乱码,你可以使用sed 鲜为人知的PtD 命令来解决这个问题:

sed '/)$/{N;s/)\n{/) {\n\ttime_exe(__func__, cl(clock()));/;t;P;D}'

Try it here !

当遇到以 ) 结尾的行时,通过在模式空间 (N) 中加载额外的行,尝试替换两行模式,然后打印 (P) 并删除 ( D) 如果模式不匹配,则从模式空间的第一行(否则t 分支到下一次迭代),将模式空间中的第二行留作下一次迭代的第一行。

使用/first line pattern/{N;s/whole pattern/replacement/} 通常就足够了,但它可能会失败,因为N 会消耗一行你不会测试第一行模式的行。这说明了here

【讨论】:

  • 你会推荐使用 awk 来代替吗? -z 在我的发行版 UNIX Mac OS 上无法识别。感谢您富有洞察力和详细的回答。
  • 我不认为awk 会很好,它也是基于行的(或至少基于记录),并且对正则表达式的使用没有多大帮助。我建议使用另一个正则表达式搜索/替换工具,我认为 glenn jackman 使用 perl 的建议是一个很好的建议,如果可用并且文件不是很大的话。否则,您的文本编辑器或您使用的 IDE 可能包含 PCRE 正则表达式引擎(例如,我可能在 Windows 上使用 Notepad++),并且可能专门的正则表达式工具可能会针对大文件进行优化。
  • 如果我没记错的话,sed(旧的默认 sed,不是 GNU 的)有一个模式空间和保持空间的概念,以及交换它们的命令。这意味着应该可以通过滚动对来处理行,但要以不可读和不可维护的 sed 脚本为代价。当 Python 不可用时,我曾经提交过这类事情。
  • @SergeBallesta 对,我认为使用D 应该可以实现,甚至不必使用保留空间。我会看看我是否可以制作出那些比 perl 更糟糕的废话;)
  • @SergeBallesta 它甚至没有我想象的那么糟糕!
【解决方案3】:

Perl 可以很好地做到这一点:

$ cat file.c
void some_function_declaration(char var1, char var2)
{
    if (a)
    { b; }
}
void func2()
{
    //
}
$ perl -0777 -pe 's {\)\n\{} {$& some other stuff;\n}g' file.c
void some_function_declaration(char var1, char var2)
{ some other stuff;

    if (a)
    { b; }
}
void func2()
{ some other stuff;

    //
}

由于将整个文件读入内存,与 Aaron's answer 的警告相同。

【讨论】:

  • 谢谢你,这行得通。我的文件不是太大,所以应该不是问题。
【解决方案4】:

我同意@Aaron,但如果你仍然想要sed,看看这个:

$  cat /tmp/del.txt
void some_function_declaration(char var1, char var2)
{
  ...
}
void enother_function_declaration(char var1, char var2)
{
  ...
}

并申请sed

$ cat /tmp/del.txt | sed ':a;N;$!ba;s/)\n{/)\{\ntime_exe(__func__, cl(clock()));/g'
void some_function_declaration(char var1, char var2){
time_exe(__func__, cl(clock()));
  ...
}
void enother_function_declaration(char var1, char var2){
time_exe(__func__, cl(clock()));
  ...
}

我认为它看起来像你想要的

UPD

让我解释一下..

更多跨平台兼容的语法是:

sed -e ':a' -e 'N' -e'$!ba' -e 's/)\n{/)\n{ ... ;/g'

在哪里

  • ':a' - 创建一个分支标签(命名为a),以后可以返回
  • 'N' - 将下一行追加到当前行(\n 之间)
  • '$!ba' - 如果下一行是最后一行,则跳转到标签 a
  • 's/)\n{/)\n{ ... ;/g' - 在单行中进行 全局 替换,由所有行和 \ns
  • 组成

【讨论】:

  • 啊,我一度以为我对N 的行为有误,但您只是重新实现-z,在应用替换之前读取整个文件。不过,如果 OP 使用的实现没有 -z
  • 老实说,当我想用​​ sed 执行新行时,除了读取整个文件之外,我不知道其他解决方案。
  • 有时,如果您匹配模式的开头,您可以测试第一行并仅使用下(几)行,但正如我在回答的最后 § 中所解释的那样在这种情况下可能是个坏主意。我以为你已经选择了这个解决方案,并且对成功测试它感到惊讶,直到我正确阅读并理解了你的 sed 命令。
  • 感谢您的详细解答。它确实帮助我掌握了更多关于 sed 的基础知识。希望这对其他人也有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-14
  • 2012-01-09
  • 2023-04-03
  • 2017-01-31
  • 1970-01-01
  • 2016-10-20
  • 1970-01-01
相关资源
最近更新 更多