【问题标题】:How to find the multiline pattern match (they must be first time match)?如何找到多行模式匹配(它们必须是第一次匹配)?
【发布时间】:2013-03-27 08:24:33
【问题描述】:

我知道这个问题How to find patterns across multiple lines using grep? 但我认为我的问题更复杂。所以我需要帮助。

我有一个字典文件BCFile as

boundary
{
    inlet
    {
        type            fixedValue;
        value           uniform (5 0 0);
    }

    outlet
    {
        type            inletOutlet;
        inletValue      $internalField;
        value           $internalField;
    }

    ....
}

我正在编写一个脚本,以便打印出inlet 边界条件fixedValueoutlet 边界条件inletOutlet

如果我使用cat BCFile | grep "type" | awk '{printf $2}' | tr -d ";",它将不起作用,因为关键字type 多次出现。

如果我使用awk -v RS='}' '/inlet/ { print $4 }' BCFile,它也不起作用,因为关键字inlet也出现了很多次。

我需要一种方法来查找首先搜索关键字 inlet 然后搜索 最接近 {} 的模式。

有谁知道如何巧妙地做到这一点?

【问题讨论】:

  • 查找具有 flag 变量的 awk 解决方案。每周都有几个出现在这里。 IE。 '/type/{t=1};/value/{v=1}; {t && v}' file(可能不完全正确,因此作为评论发布)。祝你好运。

标签: regex shell sed awk


【解决方案1】:

由于您没有为您发布的输入提供预期的输出,我们只是猜测您想要输出什么,但在 GNU awk 中如何:

$ cat tst.awk
BEGIN{ RS="\0" }
{
   print "inlet:",  gensub(/.*\yinlet\y[^}]*type\s+(\w+).*/,"\\1","")
   print "outlet:", gensub(/.*\youtlet\y[^}]*type\s+(\w+).*/,"\\1","")
}
$ gawk -f tst.awk file
inlet: fixedValue
outlet: inletOutlet

解释:

RS="\0"

= 将记录分隔符设置为 Null 字符串,以便 awk 将整个文件作为单个记录读取。

gensub(/.*\yinlet\y[^}]*type\s+(\w+).*/,"\\1","")

= 查找单词inlet 后跟除} 之外的任何字符(因此您在文件中inlet 之后的第一个} 之前停止而不是最后一个}),然后是单词@ 987654330@ 后跟空格。之后的字母数字字符串 (\w+) 是您要打印的单词,因此请记住它,然后将整个记录替换为保存在 \\1 中的字符串。

设置 RS="\0"gensub() 都是 gawk 特定的。

【讨论】:

  • 哇,我的帽子掉了。您能否在语法中添加一些解释? :)
  • @Daniel - 添加了解释。另请参阅 gawk 手册,gnu.org/software/gawk/manual/gawk.html。如果您要进行这样的文本文件操作,我强烈建议您阅读 Arnold Robbins 所著的《Effective Awk Programming, Third Edition》一书。
  • 非常感谢。我是脚本新手,在sedawk 之间进行选择有很大困难?哪个更强大或更灵活,或者具有良好且有益的学习曲​​线?谢谢
  • 与所有 UNIX 工具一样,您应该酌情使用这两种工具。 sed 是用于在单行上进行简单替换的出色工具,对于任何其他文本操作,您都应该使用 awk。你可以在 sed 中做很多你永远不应该做的事情——那是因为 sed 比 awk 早了几年,所以它有大量的语言结构可以以极其复杂的方式做事,只是因为回到过去(1970 年代初!)没有更简单的选择。您应该在 sed 中使用的唯一命令是 s、g、p(带 -n)和 d。
  • 顺便说一句,老实说 - 我可以说有经验的人仍然使用超过这 4 种结构发布 sed 解决方案的原因是因为他们只是喜欢用 sed 解决问题的挑战。我明白这一点,但我希望他们不会,因为它会误导新人认为这是一种合理的方法。
【解决方案2】:

你会使用 perl 吗?

#!/usr/bin/env perl

use strict;
use warnings;

my $filename = $ARGV[0];

open(my $f, '<', $filename) or die "Unable to open $filename: $!\n";
my $string = do { local($/); <$f> };
close($f);

$string =~ /(inlet).*type\s*(\w+).*(outlet).*type\s*(\w+)/s;
print "$1: $2\n$3: $4\n";

【讨论】:

  • 非常感谢!但是没有 shell 脚本可以做到这一点?
  • UNIX shell 是一个调用工具的环境。 perl 是一个与 sed、grep、awk 等类似的工具。唯一的区别是,与我提到的其他工具不同,perl 并非随所有 UNIX 安装一起提供。
【解决方案3】:

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

sed -rn '/^\s*(inlet|outlet)/,/^\s*}/!b;/type/s/.*\s(\S+);.*/\1/p' file

如果您将“类型”的搜索范围缩小到“入口”和下一个“}”或“出口”和下一个“}”之间,这会使整个练习变得更容易。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-11
    • 2017-11-30
    • 1970-01-01
    • 1970-01-01
    • 2017-07-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多