【问题标题】:How to use grep/awk/sed to isolate part of a file between delimiters, potentially crossing lines如何使用 grep/awk/sed 在分隔符之间隔离文件的一部分,可能会交叉线
【发布时间】:2019-09-17 10:52:56
【问题描述】:

我正在尝试使用 bash 脚本获取输出文件的特定部分,但我不知道如何继续。首先,我需要的数据在\HF=和第一次出现另一个\.之间的值用逗号隔开,但有时中间有换行符。我需要的是 grep 所有这些值并将它们发送到一个只包含它们的新文件,每行一个。

我的输出文件示例:

...\HF=-56.876868,-56.2343,-42.
343,-67.3453423,-85.74656,-
45.864\...

我尝试使用grep -Pzo,但我不知道如何使用它。

如前所述,一个值可以分为两行:

...-90.80
234,...

而且它必须被认为是同一个数字。有时只有减号在上面一行,其余的数字在下一行:

...,-
56.656,...

来自Gaussian的输出示例:

 433513773\H,-0.5821679865,0.6475216708,0.9536248473\H,-0.7834605038,0.
 4523031701,2.780055657\\Version=ES64L-G09RevD.01\HF=-156.0385049,-156.
 312885,-156.0311709,-156.0310505,-156.0309275,-156.0308023,-156.030548
 ,-156.0304151,-156.0302832,-156.0301504,-156.0300168,-15,8492,84298484
 .0385128\RMSD=4.113e-09,3.064e-09,3.538e-09,3.945e-09,9.452e-09,9.542e
 -09,9.805e-09,9.877e-09,9.916e-09,2.730e-09,3.175e-09,3.077e-09,3.301e

我需要的文件示例

-156.0385049
-156.312885
-156.0311709
-156.0310505
-156.0309275
-156.0308023
-156.030548
-156.0304151
-156.0302832
-156.0301504
-156.0300168
-156.84928429

【问题讨论】:

  • 请将该示例输入的所需输出添加到您的问题中。
  • 是的,但值是介于 \HF= 和单个反斜杠 \ 之间
  • 另外,您是否有理由不使用tr -d '\r\n' 来批量消除换行符?
  • 我使用的:sed -z 's/\n //g' gaussian.log | grep -Po '\\HF=\K[^\]*' | tr ',' '\n' >the_file_i_need.out
  • 很抱歉,我的错。我正在打开旧版本的输出,我创建的名称彼此相似。现在它是 100% 工作。感谢您的帮助

标签: bash awk grep sh


【解决方案1】:

在任何 UNIX 机器上的任何 shell 中使用任何 awk:

$ awk -v RS='\\' -F, -v OFS='\n' 'sub(/^HF=/,""){gsub(/[[:space:]]+/,""); $1=$1; print}' file
-156.0385049
-156.312885
-156.0311709
-156.0310505
-156.0309275
-156.0308023
-156.030548
-156.0304151
-156.0302832
-156.0301504
-156.0300168
-15
8492
84298484.0385128

您的示例输入中的最后一个字段似乎不是您所期望的。如果您的 awk 不支持 POSIX 字符类,例如nawk,然后只需将[[:space:]] 更改为[ \t\n]

【讨论】:

  • @vintnes 将任何内容分配给字段会导致使用字段之间的 OFS 值重建记录。因此,如果您只想用 OFS 替换所有 FS,请为其自身分配一个字段。试试echo 'a,b' | awk -F, -v OFS='#' '{print; $1=$1; print NF, $0}'。因此,在上面的代码中,我将整个记录中的,s 替换为\ns,就像我写gsub(/,/,"\n") 一样,但没有不希望的重新拆分为后者会发生的字段。将之前的 NF 与 echo 'a,b' | awk -F, -v OFS='#' '{print; gsub(/,/,"#"); print NF, $0}' 的输出中的 NF 进行比较
【解决方案2】:

这样的?

awk -F '\' '!p && $2 ~ /^HF=/ { $1=""; p=$0; next }
    p && NF>1 { p = p $1; print p; p="" }
    p { p = p $0 }' file >newfile

如果我们看到HF=,开始收集东西到p。如果设置了p,我们正在收集;继续收集,直到我们看到另一个反斜杠。当我们看到它时,打印收集到的p,然后从空的p 重新开始(即,在我们再次看到下一个开始标记之前,我们不再收集输出)。

请注意,这假设一行中的反斜杠不超过一个。如果您需要支持它,则需要稍微复杂一点的脚本。 (特别是,您不能相信 $2 包含 `HF=,但您必须遍历所有字段并检查哪一个是实际开始。)

grep 不适合这种情况,而且 Bash 本身也不是特别完善。你可以试试sed,但它更像是一种只写的语言,所以很少推荐它用于重要的任务。

【讨论】:

  • 在 \HF= 和 \ 之间只有数字和逗号
  • 有趣的问题是在同一行之前或之后是否可以有额外的反斜杠。
  • 哦,也许有。 GAUSSIAN 的输出格式不正确
  • 你在问题​​中根本没有提到这个工具,所以我以为你错误地输入了标签。 gaussian 是关于一个数学概念,而不是一个特定的工具。也许您想通过指向有关此特定工具的更多信息的链接来更新您的问题。
  • GAUSSIAN 是一款计算化学软件。在这种情况下不是高斯正态分布
【解决方案3】:

sed解决方案:

sed -En '/\HF/{
s/^.*\HF=//;
:label1
N;
/\\/!{b label1}
N;
s/[[:space:]]+//g;
s/,/\n/g;s/\\.*//gp;
q;
}' file > outfile

输出

-156.0385049
-156.312885
-156.0311709
-156.0310505
-156.0309275
-156.0308023
-156.030548
-156.0304151
-156.0302832
-156.0301504
-156.0300168
-15
8492
84298484.0385128

遗憾:在此处硬编码换行符 :(

【讨论】:

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