【问题标题】:bash looping and extracting of the fragment of txt filebash循环和提取txt文件的片段
【发布时间】:2021-01-15 06:41:16
【问题描述】:

我正在处理位于 workdir 中的大量 dlg 文本文件的分析。每个文件都有一个表格(通常位于日志的不同位置),格式如下:

文件 1:

    CLUSTERING HISTOGRAM
    ____________________


________________________________________________________________________________
     |           |     |           |     |
Clus | Lowest    | Run | Mean      | Num | Histogram
-ter | Binding   |     | Binding   | in  |
Rank | Energy    |     | Energy    | Clus|    5    10   15   20   25   30   35
_____|___________|_____|___________|_____|____:____|____:____|____:____|____:___
   1 |     -5.78 |  11 |     -5.78 |   1 |#
   2 |     -5.53 |  13 |     -5.53 |   1 |#
   3 |     -5.47 |  17 |     -5.44 |   2 |##
   4 |     -5.43 |  20 |     -5.43 |   1 |#
   5 |     -5.26 |  19 |     -5.26 |   1 |#
   6 |     -5.24 |   3 |     -5.24 |   1 |#
   7 |     -5.19 |   4 |     -5.19 |   1 |#
   8 |     -5.14 |  16 |     -5.14 |   1 |#
   9 |     -5.11 |   9 |     -5.11 |   1 |#
  10 |     -5.07 |   1 |     -5.07 |   1 |#
  11 |     -5.05 |  14 |     -5.05 |   1 |#
  12 |     -4.99 |  12 |     -4.99 |   1 |#
  13 |     -4.95 |   8 |     -4.95 |   1 |#
  14 |     -4.93 |   2 |     -4.93 |   1 |#
  15 |     -4.90 |  10 |     -4.90 |   1 |#
  16 |     -4.83 |  15 |     -4.83 |   1 |#
  17 |     -4.82 |   6 |     -4.82 |   1 |#
  18 |     -4.43 |   5 |     -4.43 |   1 |#
  19 |     -4.26 |   7 |     -4.26 |   1 |#
_____|___________|_____|___________|_____|______________________________________

目的是遍历所有 dlg 文件并从对应于更广泛集群的表中获取单行(直方图列中的斜线数量更多)。在上面的表格示例中,这是第三行。

   3 |     -5.47 |  17 |     -5.44 |   2 |##

然后我需要将此行与日志文件的名称(应在该行之前指定)一起添加到 final_log.txt 中。所以最后我应该有以下格式的东西(3个不同的日志文件):

"Name of the file 1": 3 |     -5.47 |  17 |     -5.44 |   2 |##
"Name_of_the_file_2": 1 |     -5.99 |  13 |     -5.98 |  16 |################
"Name_of_the_file_3": 2 |     -4.78 |  19 |     -4.44 |   3 |###

我的 BASH 工作流程的一个可能模型是:

#!/bin/bash
do
  file_name2=$(basename "$f")
  file_name="${file_name2/.dlg}"
  echo "Processing of $f..."
  # take a name of the file and save it in the log
  echo "$file_name" >> $PWD/final_results.log
  # search of the beginning of the table inside of each file and save it after its name
  cat $f |grep 'CLUSTERING HISTOGRAM' >> $PWD/final_results.log
  # check whether it works
  gedit $PWD/final_results.log
done

这里我需要替换 echo 和 grep 的组合以获取表格的选定部分。

【问题讨论】:

  • 如果出现平局,应该收集哪一行?
  • 您的意思是示例嵌入在更大的文件中吗?输入文件中可以有多个这些片段吗?我们如何区分这些行和应该忽略的行?
  • 对!排名表位于日志文件中的某处,因此行号可能不同;-) 以下格式只有一个表。为简单起见,我们可以尝试处理示例工作流,例如从表中获取第一行 ;-))
  • 我回滚了您的编辑。在您收到原始问题的多个答案后,请不要移动目标帖子。如果您仍然无法解决问题,(可能接受您在此处获得的答案之一,并且)发布一个新问题,其中包含实际的代表性数据和 minimal reproducible example
  • 这显然是现在的后续问题:stackoverflow.com/questions/64140486/…

标签: bash loops logging awk sed


【解决方案1】:

你可以使用这个,预计会足够快。除了表格之外,文件中的额外行预计不会成为问题。

grep "#$" *.dlg | sort -rk11 | awk '!seen[$1]++'

grep 获取所有直方图行,然后按最后一个字段以相反的顺序排序,这意味着顶部有最多# 的行,最后awk 删除重复项。注意grep解析多个文件时,默认-H在行首打印文件名,所以如果你测试一个文件,使用grep -H

结果应该是这样的:

file1.dlg:   3 |     -5.47 |  17 |     -5.44 |   2 |##########
file2.dlg:   3 |     -5.47 |  17 |     -5.44 |   2 |####
file3.dlg:   3 |     -5.47 |  17 |     -5.44 |   2 |#######

这是一个修改,以便在文件中有许多相等的最大行的情况下首次出现:

grep "#$" *.dlg | sort -k11 | tac | awk '!seen[$1]++'

我们将排序中的 reversed 参数替换为反转文件流的“tac”命令,因此现在对于任何相等的行,都会保留初始顺序。


第二种解决方案

这里只使用awk:

awk -F"|" '/#$/ && $NF > max[FILENAME] {max[FILENAME]=$NF; row[FILENAME]=$0}
           END {for (i in row) print i ":" row[i]}' *.dlg

更新:如果您从不同的目录执行它并且只想保留每个文件的基本名称,请删除路径前缀:

awk -F"|" '/#$/ && $NF > max[FILENAME] {max[FILENAME]=$NF; row[FILENAME]=$0}
           END {for (i in row) {sub(".*/","",i); print i ":" row[i]}}'

【讨论】:

  • 一般来说,您的解决方案非常简单,而且效果非常好!我注意到的是:如果我使用 grep(不带 -H):它会从每个文件中提供几行(表中的),根据 # 的数量排序...如果我使用 Grep -H:它会给出我的需要但首先它没有给出文件的名称,而是给出它所在的完整路径......所以.. :-)
  • 对不起,我没有完全理解最后一点。是否每次在 DIR 中都需要 CD 以避免结果日志中出现完整路径?实际上,我只是将您的一行解决方案放在了我的循环中,并且它在功能上运行良好:我将编辑我的第一个主题,它在我的情况下是如何工作的。再次感谢您。
  • 如果这样更方便的话,您可以轻松地将简单的sub 添加到 Awk 脚本中以修剪文件名的开头,但代价是使脚本稍微不那么优雅。提示:sub(/.*\//, "", $1) 删除直到 $1 中最后一个斜杠的所有内容。
  • 我也刚刚注意到您的解决方案存在一个小问题(无论如何都非常好!!)。实际上,如果表中有几行具有相同的 # 编号,则脚本将采用具有相同 # 的 BOTTOM 行。是否可以在最后一列中第一次出现 # 数量最多的行?为了清楚起见,想象在表格中(从第一篇文章开始)有 10 行都带有#,在这种情况下,我需要取第 1 行。:-)
  • sort 将有效地打乱输出行的顺序,因此 Awk 无法知道它们在输入中的顺序是什么(因此不特别喜欢第一条或最后一条);但是您可以通过添加辅助排序键来稳定它;也许sort -k11:11r,1:1,2:2n
【解决方案2】:

作为 Awk 脚本可能更有意义。

这会在输入文件中出现平局的情况下选择具有最宽直方图的第一行。

#!/bin/bash

awk 'FNR == 1 { if(sel) print sel; sel = ""; max = 0 }
   FNR < 9 { next }
   length($10) > max { max = length($10); sel = FILENAME ":" $0 }
   END { if (sel) print sel }' ./"$prot"/*.dlg

这假设直方图总是第十个字段;如果您的输入格式比您显示的内容更混乱,也许可以适应口味。

更详细地说,第一行在每个输入文件的第一行触发。如果我们收集了前一行(这意味着这不是第一个输入文件),打印它,然后重新开始。否则,初始化第一个输入文件。将sel 设置为空,将max 设置为零。

第二行跳过包含标题的第 1-8 行。

第三行检查当前行的直方图是否长于max。如果是,请将max 更新为此直方图的长度,并记住sel 中的当前行。

当我们处理完所有文件时,最后一行是溢出的。我们从来没有从最后一个文件中打印出sel,所以也打印它,如果它已经设置好了。

如果你的意思是说我们应该找到CLUSTERING HISTOGRAM 和表格末尾之间的线,我们可能应该有更多关于周围线是什么样子的信息。不过可能是这样的;

awk '/CLUSTERING HISTOGRAM/ { if (sel) print sel; looking = 1; sel = ""; max = 0 }
   !looking { next }
   looking > 1 && $1 != looking { looking = 0; nextfile }
   $1 == looking && length($10) > max { max = length($10); sel = FILENAME ":" $0 }
   END { if (sel) print sel }' ./"$prot"/*.dlg

当我们看到CLUSTERING HISTOGRAM 时,这会将looking 设置为1,然后计数到looking 不再增加的第一行。

【讨论】:

  • 它将某些内容保存到我的最终日志中,但不是正确的行。我想我需要 SED 和 AWK 的组合来通过 SED 在 *.dlg 文件中查找表(通常是文件中随机位置中的表),然后只保存其中的行:-)
  • 我不明白你想说什么,但你通常不需要将sed 与 Awk 结合使用。如果您想替换某些东西,请使用 awk 的 gsub 函数。
  • 哦,你需要跳过标题。它总是一样长吗?添加了快速解决方法
  • 抱歉造成误会!是的,该表始终以以下格式显示,但实际上它可能位于日志文件中稍有不同的位置!我只需要表格中的一行,更具体地说是第二列中出现的负数;-)
  • 这一切都相当混乱。也许在这里接受一个答案(或发布您自己的答案并接受)并提出一个新问题,并正确说明实际要求,并提供您目前无法使用您拥有的工具处理的代表性示例。
【解决方案3】:

我建议使用 awk 进行处理:

for i in $FILES
do
    echo -n \""$i\": "
    awk 'BEGIN {
           output="";
           outputlength=0
         }
         /(^ *[0-9]+)/ {                                    # process only lines that start with a number
           if (length(substr($10, 2)) > outputlength) {     # if line has more hashes, store it
             output=$0;
             outputlength=length(substr($10, 2))
           }
         }
         END {
           print output                                     # output the resulting line
         }' "$i"
done

【讨论】:

  • 它没有选择正确的片段。你能给我一个 AWK 的例子吗?我的循环只会在每个 *.dlg 文件中找到“关键字”,然后将它复制到 final_result.log 文件中?谢谢!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-27
  • 2023-01-20
  • 1970-01-01
相关资源
最近更新 更多