【问题标题】:Search for first item in line, then print line and following line搜索行中的第一项,然后打印行和下一行
【发布时间】:2012-11-19 07:36:34
【问题描述】:

我正在尝试在文件中搜索名称,然后打印以下行。我最初是这样解决的:

grep -A 1 "searchterm" filename

但是,这会在行中的任何地方搜索searchterm;这是一个问题,因为我只希望在该行的第一部分匹配。

例如,如果我在以下文件中查找1234

4567 otherstuff 1234
wrongsecondline
1234 otherstuff
rightsecondline

它会找到4567 otherstuff 1234wrongsecondline,而我真正想要的是1234 otherstuffrightsecondline

关于如何仅搜索一行中的第一项,然后打印该行和第二行的任何想法?谢谢!

【问题讨论】:

  • grep 接受正则表达式作为搜索词。您应该在该术语前加上 ^ 并以 $ 结尾,例如^1234 其他东西$

标签: unix awk grep


【解决方案1】:

假设您不想增强选择标准,则使用 grep 是完全合理的,但仅供参考,以下成语描述了如何使用 awk 选择给定特定模式的记录范围进行匹配:

a) Print all records from some pattern:

    awk '/pattern/{f=1}f' file

b) Print all records after some pattern:

    awk 'f;/pattern/{f=1}' file

c) Print the Nth record after some pattern:

    awk 'c&&!--c;/pattern/{c=N}' file

d) Print every record except the Nth record after some pattern:

    awk 'c&&!--c{next}/pattern/{c=N}1' file

e) Print the N records after some pattern:

    awk 'c&&c--;/pattern/{c=N}' file

f) Print every record except the N records after some pattern:

    awk 'c&&c--{next}/pattern/{c=N}1' file

g) Print the N records from some pattern:

    awk '/pattern/{c=N}c&&c--' file

我将变量名从“find”的“f”更改为“count”的“c”,因为这样更能表达变量的实际含义。

因此,对于这种情况,您可以使用上面的成语“c”:

awk 'c&&!--c;/1234/{c=1}' file

【讨论】:

    【解决方案2】:

    使用grep 打印匹配的行和以下行:

    $ egrep -w -A1 "^1234" filename
    1234 otherstuff
    rightsecondline
    

    使用awk实现和上面一样

    $ awk '$1=="1234"{print;getline;print}' filename
    1234 otherstuff
    rightsecondline
    

    使用grep打印匹配后的行(注意<filename之前):

    $ grep -w -H --label=dummy -A1 '^1234' <filename | sed -ne 's#^dummy-##p'
    rightsecondline
    

    使用awk实现和上面一样:

    $ awk '$1=="1234"{getline;print}' filename
    rightsecondline
    

    †前提是没有连续两行包含搜索词,并且文件中的最后一行不包含搜索词


    如果您希望两个或多个连续的行包含搜索词,例如

    4567 otherstuff 1234
    wrongsecondline
    1234 otherstuff once
    1234 otherstuff again
    rightsecondline
    

    ...然后有状态地使用awk 来实现与grep -A1 相同的输出:

    $ awk 'pr_after{print;pr_after=0}$1=="1234"{print;pr_after=1}' filename
    1234 otherstuff once
    1234 otherstuff again
    rightsecondline
    

    ...并有状态地使用awk 始终打印匹配后的行,即使该行本身就是匹配:

    $ awk 'pr_after{print;pr_after=0}$1=="1234"{pr_after=1}' filename
    1234 otherstuff again
    rightsecondline
    

    ...或有状态地使用awk 仅打印紧跟在一个或多个匹配行之后的非匹配行,实现与上述grep -H | sed 相同的输出:

    $ awk '$1=="1234"{pr_after=1;next}pr_after{print;pr_after=0}' filename
    rightsecondline
    

    在上面的例子中,$1=="1234"{...} 是一个pattern/action rule,这意味着如果第一列等于文本1234 然后执行...pr_after{...} 意味着如果变量pr_after 设置为某个非零非空值然后执行 ...getline 表示 读取下一行并继续执行 getline 之后的语句,而 @ 987654323@ 表示读取下一行并在第一个模式处重新开始评估

    【讨论】:

    • 太棒了!两者都完美无缺。我喜欢 grep 解决方案,因为它更快。谢谢!!
    • 除非您完全理解所有的许多注意事项,否则不要使用 getline,请参阅awk.info/?tip/getline。您已经通过无法匹配包含 1234 的连续行来发现其中一个,现在尝试使用最后一行是 1234 的文件,您会看到另一个(该匹配行将被打印 [两次] 而不是非- 后面的行)。
    • @EdMorton 我几乎不会称它们为警告,我实际上看不到您希望getline 工作的方式else(就像non-existent peekline?。)测试getline 的返回值(例如if(getline)print)可以很好地处理文件中最后一行的情况,但同样,上面的getline 示例针对格式正确的输入(例如,每个匹配类型A 记录总是后跟 B 类记录)并且在该上下文中意味着简单。有状态的示例在所有情况下都是正确的,但更复杂。
    • getline 以它的工作方式工作,我并不是说它应该改变,只是如果你要使用它,你必须知道它是如何工作的。在这种情况下,是的,至少对其返回代码进行测试以解决当前的问题。您曾说过 awk/getline 方法与 grep 方法实现了相同的效果,但在不明显但非常真实的情况下并非如此,因此值得说明这些是什么,因此 OP 不会将所述 grep 等效性视为精确.
    • 顺便说一下,if (getline) 不是如何测试成功的 getline 返回,您需要测试它的返回代码是否大于零,因为对于某些失败情况它可能小于零。再次,请参阅awk.info/?tip/getline
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多