【问题标题】:Print previous line if condition is met如果满足条件,打印上一行
【发布时间】:2015-02-28 17:06:27
【问题描述】:

我想 grep 一个单词,然后找到该行中的第二列并检查它是否大于一个值。是的,我要打印上一行。

例如:

输入文件

AAAAAAAAAAAAA
BB  2
CCCCCCCCCCCCC
BB 0.1

输出

AAAAAAAAAAAAA

现在,我想搜索 BB,如果该行中的第二列(2 或 0.1)大于 1,我想打印上一行。

有人可以帮我处理 grep 和 awk 吗?谢谢。也欢迎任何其他建议。谢谢。

【问题讨论】:

  • 这个问题每隔一天问一次。

标签: linux awk grep


【解决方案1】:

这可以是一种方式:

$ awk '$1=="BB" && $2>1 {print f} {f=$1}' file
AAAAAAAAAAAAA

说明

  • $1=="BB" && $2>1 {print f} 如果第一个字段正好是BB 并且第二个字段大于1,则打印f,一个存储的值。
  • {f=$1} 将当前行存储在f 中,以便在读取下一行时可以访问。

【讨论】:

  • 它只是在 linux 中为我返回第一个单词
  • @AbhishekRanjan 给定的输入只包含一个单词,所以我刚刚抓住了$1。如果要存储整行,只需将{f=$1} 替换为{f=$0}
  • 很精彩但很奇怪。 '{f=$1}' 怎么可能是上一行,而 '$1=="BB".. 中的 $1 处理当前行,即第二行?
  • @JoonhoPark 因为在处理下一行之前我们不会打印f。这里的关键点是print 在哪里。
  • 哇,这太花哨了。这可以处理两行甚至比较两行中的任何字段。这就是我要找的。​​span>
【解决方案2】:

另一种选择:如果条件匹配,则反转文件并打印 下一行

tac file | awk '$1 == "BB" && $2 > 1 {getline; print}' | tac

【讨论】:

  • 这种方法在比赛前需要多行时更有用,但我认为这可能有点矫枉过正。
【解决方案3】:

关于一般性

我认为需要提到的是,这类问题最通用的解决方案涉及两个pass:

  • 第一次将十进制行号 ($REC) 添加到每行的前面,有效地将行按 $REC 分组为记录
  • 第二遍触发 $REC 的每个新值的第一个实例作为记录边界(重置 $CURREC),然后在有关匹配 $CURREC 的记录的本机 AWK 习语中滚动。

在中间文件中,一些十进制数字序列后跟一个分隔符(出于人为原因,通常是一个添加的制表符或空格)被解析(也就是概念上的剪断)作为相对于基线文件的带外。

命令行粘贴怪物

即使仅限于命令行,确保中间文件永远不会撞到磁盘也是一件容易的事。您只需要使用支持进程替换的高级 shell,例如 ZSH(我自己最喜欢的):

paste <( <input.txt awk "BEGIN { R=0; N=0; } /Header pattern/ { N=1; } { R=R+N; N=0; print R; }" ) input.txt | awk -f yourscript.awk 

让我们把那条线更适合展示:

P="/Header pattern/"
X="BEGIN { R=0; N=0; } $P { N=1; } { R=R+N; N=0; print R; }"
paste <( <input.txt awk $X ) input.txt | awk -f yourscript.awk 

这将启动三个进程:简单的内联 AWK 脚本paste,以及您最初真正想要运行的 AWK 脚本。

在幕后,&lt;() 命令行构造创建了一个命名管道,并将管道名称作为其第一个输入文件的名称进行粘贴。对于paste 的第二个输入文件,我们将其命名为原始输入文件的名称(因此,此文件由两个不同的进程按顺序并行读取,它们之间最多会消耗一个 从磁盘读取,如果输入文件是冷的)。

中间的神奇命名管道是一个内存中 FIFO,古代 Unix 可能以大约 16 kB 的平均大小管理它(如果yourscript.awk 进程在排空这个 FIFO 时速度缓慢,则会间歇性地暂停 paste 进程)。

也许现代 Unix 在那里抛出了一个更大的缓冲区,因为它可以,但它肯定不是你应该关心的稀缺资源,直到你编写你的第一个 真正 高级命令行,其中包含涉及这些的进程重定向成百上千:-)

其他性能注意事项

在现代 CPU 上,所有这三个进程都可以很容易地发现自己在不同的内核上运行。

这些过程中的前两个实际上是微不足道的:一个带有单个模式匹配和一些小记账的 AWK 脚本,使用两个参数调用粘贴。 yourscript.awk 将很难跑得比这些更快。

什么,您的开发机器没有轻负载的内核,无法在执行域中几乎免费地呈现这种主要的 shell-master 解决方案模式?

响,响。

喂?

嘿,这是给你的。 2018 年刚刚打来电话,希望解决问题。

2020 年正式成为 MTV 的缓刑期:这就是我们喜欢的方式,魔法管道免费,核心免费。更不用说最近在这个领域摇摆不定的任何特定 TLA 芯片供应商了。

作为最终的性能考虑,如果您不想要解析实际记录号的开销:

X="BEGIN { N=0; } $P { N=1; } { print N; N=0; }"

现在,您的 in-FIFO 中间文件仅在每行前附加两个字符('0' 或 '1' 以及由paste 添加的默认分隔符)进行注释,其中 '1' 标记第一行记录。

命名的 FIFO

在底层,这些与 Unix 在您编写任何普通管道命令时实例化的魔法 FIFO 没有什么不同:

cat file | proc1 | proc2 | proc2 

三个未命名的管道(以及一个专门用于 cat 的整个过程,您甚至不需要)。

不幸的是,由 shell 预先管理的默认 stdin/stdout 流的真正异常便利性掩盖了paste $magictemppipe1 $magictemppipe2 没有额外的性能考虑值得考虑的现实,在 99%所有情况。

“使用&lt;() Y 型接头,卢克。”

您对问题域中自然语义分解的本能反应将因此受益匪浅。

如果有人一开始就将外壳构造 &lt;() 命名为 YODA 运算符,我怀疑它至少在十年前就会被压入通用服务中。

【讨论】:

    【解决方案4】:

    结合 sed 和 awk 你会得到这个: sed 'N;s/\n/ /' &lt; file |awk '$3&gt;1{print $1}'

    sed 'N;s/\n/ / : 合并第一行和第二行,并将下一行字符替换为空格

    awk '$3&gt;1{print $1}': 如果 $3(第 3 列的值 > 1),则打印 $1(第 1 列)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-20
      • 1970-01-01
      相关资源
      最近更新 更多