基于子行文件的一行和打印其他行直到它到达下一行

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于子行文件的一行和打印其他行直到它到达下一行相关的知识,希望对你有一定的参考价值。

我有一个文本文件,其中包含多个条目,如下所示:

# 2018 11 21 17 47 37.708756 -34.390213 116.803673 2.6972 0.442474 3.324627 2.840390 0.885880 890
LM01 0.836408 1.00 P
LM01 1.035398 1.00 S
LM03 3.987074 1.00 S
# 2018 11 22 11 58 25.550581 -34.439400 116.750832 2.8513 0.288144 3.306790 2.576028 0.771026 891
LM01 1.664419 1.00 P
LM01 2.471786 1.00 S
LM03 3.536432 1.00 P
# 2018 11 22 14 38 7.190175 -34.447819 116.788727 3.1661 0.577347 2.063253 2.132511 0.608057 892
LM01 1.629825 1.00 P
LM02 3.059825 1.00 P
LM03 3.284825 1.00 P
LM01 2.378885 1.00 S

我需要提出一种方法,最好是在Bash或Perl中,用#读取行,基于第8列(纬度)排列的子集,如果条件满足,则打印其余行(例如LM。 ..)直到它与#到达下一行。例如,我只想打印第8列<-34.4的“条目”,并包含该条目的LM *行。

我可以拿出代码来读取每个#线,但我不确定如何编程“如果满足条件,打印LM线直到你到达下一个#行”。预期的产出是:

# 2018 11 22 11 58 25.550581 -34.439400 116.750832 2.8513 0.288144 3.306790 2.576028 0.771026 891
LM01 1.664419 1.00 P
LM01 2.471786 1.00 S
LM03 3.536432 1.00 P
# 2018 11 22 14 38 7.190175 -34.447819 116.788727 3.1661 0.577347 2.063253 2.132511 0.608057 892
LM01 1.629825 1.00 P
LM02 3.059825 1.00 P
LM03 3.284825 1.00 P
LM01 2.378885 1.00 S
答案

gawk记录分隔符,perl应该有类似的...

$ awk -v RS='(^|
)#' '$7<-34.4{printf "%s", rt $0} {rt=RT}' file

# 2018 11 22 11 58 25.550581 -34.439400 116.750832 2.8513 0.288144 3.306790 2.576028 0.771026 891
LM01 1.664419 1.00 P
LM01 2.471786 1.00 S
LM03 3.536432 1.00 P
# 2018 11 22 14 38 7.190175 -34.447819 116.788727 3.1661 0.577347 2.063253 2.132511 0.608057 892
LM01 1.629825 1.00 P
LM02 3.059825 1.00 P
LM03 3.284825 1.00 P
LM01 2.378885 1.00 S

请注意,您需要<,因为该标志是否定的。由于我们使用#作为记录分隔符,因此字段数减少一个。

我们将记录分隔符定义为前导#或后面的新行。通常RS在记录之间,但在这里它是领先的记录。这就是为什么我们捕获匹配的记录分隔符RT并分配给(下一个)记录中使用的变量。 RT也包括新线,这就是为什么printf没有。

另一答案

如果标志打开,则在不以#打印的行上打印,否则根据条件设置标志(和打印)

perl -wlnE'
    if (/^s*[^#]/) { say if $y } elsif ((split)[7] < -34.4) { $y=1, say }
' file

使用file中提供的样本输入,可以打印预期的输出。

标志-lnE可以是-ne,代码中使用print而不是say-w仅用于警告,通常在单行中省略(我总是使用它)。见Command switches in perlrun

另一答案
perl -lane '$matches = ($F[7] < -34.4); print if ($matches .. (/^#/ and not $matches)) and ($matches or not /^#/)'

这有点牵扯。你可以让$matches成为你想要的# ...线上的任何表达。 ($matches .. (/^#/ and not $matches))匹配所有标题行,包括下一个(可能不匹配)标题,然后and ($matches or not /^#/)排除任何不匹配的标题。

..是专为这些用例而设计的Range Operator

另一答案

另一个Perl单线

  perl -0777 -ne ' while( /(^#.+?)(?=^#|)/gsm ) { print $1 if (split(" ",$1))[7] < -34.4 } '

输入

$ cat geeb.txt
# 2018 11 21 17 47 37.708756 -34.390213 116.803673 2.6972 0.442474 3.324627 2.840390 0.885880 890
LM01 0.836408 1.00 P
LM01 1.035398 1.00 S
LM03 3.987074 1.00 S
# 2018 11 22 11 58 25.550581 -34.439400 116.750832 2.8513 0.288144 3.306790 2.576028 0.771026 891
LM01 1.664419 1.00 P
LM01 2.471786 1.00 S
LM03 3.536432 1.00 P
# 2018 11 22 14 38 7.190175 -34.447819 116.788727 3.1661 0.577347 2.063253 2.132511 0.608057 892
LM01 1.629825 1.00 P
LM02 3.059825 1.00 P
LM03 3.284825 1.00 P
LM01 2.378885 1.00 S
$ perl -0777 -ne ' while( /(^#.+?)(?=^#|)/gsm ) { print $1 if (split(" ",$1))[7] < -34.4 } ' geeb.txt
# 2018 11 22 11 58 25.550581 -34.439400 116.750832 2.8513 0.288144 3.306790 2.576028 0.771026 891
LM01 1.664419 1.00 P
LM01 2.471786 1.00 S
LM03 3.536432 1.00 P
# 2018 11 22 14 38 7.190175 -34.447819 116.788727 3.1661 0.577347 2.063253 2.132511 0.608057 892
LM01 1.629825 1.00 P
LM02 3.059825 1.00 P
LM03 3.284825 1.00 P
LM01 2.378885 1.00 S
$

以上是关于基于子行文件的一行和打印其他行直到它到达下一行的主要内容,如果未能解决你的问题,请参考以下文章

HTML:如何使用 AngularJs 填充第一行是父行和后续行是子行的行

如何阅读直到 EOF 并打印输入的偶数/奇数?

PowerQuery - 主行和子行 - 基于公式或逻辑的另一行的参考值

逐行读取文件

从行上的单元格值添加前缀

如何使一行代码在循环中运行一次,并且只运行下一行代码,直到前一行运行一次?