如果满足条件则打印上一行

Print previous line if condition is met

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

例如:

输入文件

AAAAAAAAAAAAA
BB  2
CCCCCCCCCCCCC
BB 0.1

输出

AAAAAAAAAAAAA

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

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

这可以是一个方法:

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

说明

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

另一种选择:反转文件并在条件匹配时打印行:

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

结合使用 sed 和 awk 你会得到这个: sed 'N;s/\n/ /' < file |awk '>1{print }'

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

awk '>1{print }':如果 $3(第 3 列的值 > 1)

,则打印 $1(第 1 列)

关于普遍性

我认为需要提及的是,这个 class 问题的最一般解决方案涉及两次传递:

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

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

命令行粘贴怪物

即使局限于命令行,也很容易确保中间文件永远不会到达磁盘。您只需要使用支持进程替换的高级 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 脚本。

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

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

也许现代 Unix 会在其中设置更大的缓冲区,因为它可以,但它肯定不是您应该关注的稀缺资源,直到您编写第一个 真正 高级命令行流程重定向涉及成百上千的这些 :-)

其他性能注意事项

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

这些过程中的前两个接近于真正微不足道的:一个 AWK 脚本,具有单个模式匹配和一些小的簿记,使用两个参数调用 paste。 yourscript.awk 将很难运行 比这些更快。

什么,你的开发机器没有轻载核心来呈现这个主 shell-master 解决方案模式在执行域中几乎是免费的?

Ring, ring.

Hello?

Hey, it's for you. 2018 just called, and wants its problem back.

2020 年正式是 MTV 的缓刑期:这就是我们喜欢的方式,魔法管道免费,核心免费。不要大声说出最近正在摇摆 space 的任何特定 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% 的情况下,没有其他值得考虑的性能考虑因素。

"Use the <() Y-joint, Luke."

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

如果有人有智慧将 shell 结构 <() 命名为 YODA 运算符,我怀疑至少在十年前它就会被压入普遍服务.