awk next 和模式匹配

Posted

技术标签:

【中文标题】awk next 和模式匹配【英文标题】:awk next and pattern match 【发布时间】:2015-03-19 00:19:55 【问题描述】:

如果我们有以下csv文件,我们只想得到“DELTA Energy Terns”部分中的$9,不包括以“Frame”开头的行

Ligand Energy Terms
Frame #,VDWAALS,EEL,EGB,ESURF,ESCF,G gas,G solv,TOTAL
0,0.0,0.0,-37.2465,2.70257904,98.8916,0.0,-34.54392096,64.34767904
1,0.0,0.0,-33.1958,2.71419624,80.6403,0.0,-30.48160376,50.15869624

DELTA Energy Terms
Frame #,VDWAALS,EEL,EGB,ESURF,ESCF,DELTA G gas,DELTA G solv,DELTA TOTAL
0,-43.3713,0.0,44.4036,-5.24443392,-27.4605,-43.3713,39.15916608,-31.67263392
1,-43.7597,0.0,37.343,-5.1764544,-23.3471,-43.7597,32.1665456,-34.9402544
2,-42.5618,0.0,44.0748,-5.2738956,-26.6719,-42.5618,38.8009044,-30.4327956
3,-43.1034,0.0,41.3681,-5.25029544,-27.1501,-43.1034,36.11780456,-34.13569544

期望的输出:

-31.6726
-34.9402
-30.4327
-34.1356

以下尝试将打印出所有 9 美元,包括“配体能量条款”部分中的 9 美元。

awk -F, '$1 ~ /DELTA Energy Terms/ next $1 ~ /Frame/ next printf("%24.4f\n",$9)'

awk -F, '$1 ~ /DELTA Energy Terms/ next  printf("%24.4f\n",$9)'

有哪位大师能开导吗?

【问题讨论】:

【参考方案1】:

以下应该可以解决问题

awk -F, '/^DELTA/ capture=1 /Energy Terms$/ next /^Frame/ next (capture) print $9'

我使用capture 标志来控制是否应捕获单个记录。默认情况下,capture 为零。当DELTA Energy Terms 行被解析时,我开始捕获。我跳过任何以Energy Terms 结尾或以Frame 开头的行。否则,如果我们在“捕获”,那么我就带出第九个元素。

如果您经常使用此脚本,我建议您使用类似于以下脚本的内容:

#!/usr/bin/awk -f
BEGIN 
    FS = ","

/^DELTA Energy Terms/ 
    capture = 1;
    next

/Energy Terms$/ 
    capture = 0;
    next

/^Frame/  next 
(capture)  print $9 

将脚本保存为extract-delta 并使其可执行,然后您可以像使用任何其他shell 命令一样使用它:

$ cat input-file | tr -d '\015' | ./extract-delta
-31.67263392
-34.9402544
-30.4327956
-34.13569544

【讨论】:

这个工作几乎完美!如果我们执行 "awk -F, '/^DELTA/ capture=1 /Energy Terms$/ next /^Frame/ next (capture) print $9' input > check.dat" 将会有每行末尾的尾随 (^M)。我可以知道这背后的任何原因吗? 这通常是由 DOS 风格的行尾字符 (\r\n) 引起的。 \r 转换为 Ctrl+M,\n 转换为 Ctrl+J。您可以通过tr -d '\015'过滤输入以去除回车字符。 谢谢!我可以知道是否有“捕获”的任何 awk 手册?搜索后我找不到任何东西。它看起来很强大,很想深入研究它。 capture 只是一个变量,它为输入的一部分启用print $9 操作。 嗨 D. Shawley,我可以知道为什么括号 capture=1 和 (capture) 不同吗?这种“捕获标志”技术有什么术语吗?【参考方案2】:

您也可以使用 bash 完成此操作,使用以下方法:

tail -n +$((2 + $(grep -n "DELTA Energy Terms" input.txt | cut -d":" -f1) )) input.txt | cut -d"," -f9

tail -n +$((2 + $(grep -n "DELTA Energy Terms" input.txt 部分将打印输入文件的行,从包含 DELTA 能量项 加 2 的行开始,然后cut 将为您提供您正在查找的第 9 个字段为了。

【讨论】:

【参考方案3】:

你可以试试下面的 awk 命令。

$ awk -v RS="\n\n" -v FS="\n" '/^DELTA Energy Terms/for(i=3;i<=NF;i++)split($i, a, /,/);print a[9]' RS=  file
-31.67263392
-34.9402544
-30.4327956
-34.13569544
RS="\n\n",所以一个空行设置为记录分隔符。 FS="\n",换行符设置为字段分隔符。 /^DELTA Energy Terms/ 如果记录以^DELTA Energy Terms 开头,则对该特定记录执行以下操作。 for(i=3;i&lt;=NF;i++)split($i, a, /,/);print a[9] 遍历除 1 和 2 之外的所有字段,然后根据逗号拆分每个字段,然后将吐出的项目存储到名为 a 的数组中。 print a[9] 打印关联数组 a 中第 9 个索引处的元素。

【讨论】:

【参考方案4】:

所有这些解决方案都有效,因此解决了眼前的问题,但没有一个回答隐含的问题。

要查看有问题的命令,为什么这不起作用?

'$1 ~ /DELTA Energy Terms/ next $1 ~ /Frame/ next printf("%24.4f\n",$9)

让我们分解一下。

# Skip every line where the first field matches. 
$1 ~ /DELTA Energy Terms/ next 
  # No line matches this criteria, so this has no effect. 
  # Explanation: The field separator isn't set, so defaults to breaking fields on white space. 
  # If you print out the first field, you will see "DELTA" on this line, not "DELTA Energy Terms".

# Skip every line where the first field matches "Frame". 
$1 ~ /Frame/ next 
  # This matches and gets skipped.

# Print every line that didn't get skipped.
printf("%24.4f\n",$9)
  # The two "Energy Terms" title lines don't have any entries in field 9, 
  # so it prints blanks for those lines.

【讨论】:

以上是关于awk next 和模式匹配的主要内容,如果未能解决你的问题,请参考以下文章

一行中的多个awk模式匹配

在 AWK 模式中添加当前日期和时间与通配符匹配

如何匹配和删除模式中的字符串

如何匹配awk中变量中给出的模式?

使用 sed 或 awk 按照匹配模式打印一行

awk 在匹配模式之前打印整行