我正在处理位于不同目录中的大量 csv 数据文件的后处理。每个 csv 文件具有以下 3 列格式:
ID, POP, dG
1, 24, -6.8100
2, 22, -6.7900
3, 11, -6.6800
4, 18, -6.1100
5, 5, -6.0700
6, 1, -6.0600
7, 11, -6.0300
8, 36, -6.0100
以下 bash 函数包含 awk 代码,该代码一次计算所有已处理 CSV的 dG(第 3 列,始终为负浮点数)的最小值以及 POP(第 2 列 2,为正)值的最大值并存储它在第二个 awk 脚本使用的新 bash 变量最高POP最低DG中(此处不考虑):
home="$PWD"
# folder with the outputs
rescore="${home}"/rescore
# folder with the folders to analyse
storage="${home}"/results_bench
cd "${storage}"
# pattern of the csv file located inside each of sub-directory of "${storage}"
str='*str1.csv'
rescore_data4 () {
str_name=$(basename "${str}" .csv)
mkdir -p "${rescore}"/"${str_name}"
# 1- calculate max POP and dGmin for ALL rescored CSVs at once
read highestPOP lowestDG < <(
awk -F ', ' '
FNR == 1 {
next
}
NR == 2 || $2 > popMAX {popMAX = $2}
NR == 2 || $3 < dGmin {dGmin = $3}
END {printf "%d %.2f\n", popMAX, dGmin}
' "${storage}"/*_*_*/${str}
)
#
# 2- run rescoring routine using the min/max values
awk -F', *' -v OFS=', ' -v highest_POP="${highestPOP}" -v lowest_dG="${lowestDG}" '
... some awk code
'
}
在第一个 awk 脚本中,$str 是位于不同目录中的目标 csv 文件的 glob 掩码(匹配 glob 模式“ _ _*”)虽然这通常有效,但第一个 AWK 代码中有一个错误(用于计算 min/所有已处理 CSV 的最大值):有时在输入 CSV 数量大/包含许多行的情况下,无法计算最低 DG 的值。问题总是与计算 dg 变量(始终为负)有关,脚本报告 dg=0.000,这是不正确的。
为了解决这个问题,我尝试修改 AWK 代码,在开始时定义两个新变量(具有最小值和最大值),然后将列中的每个值与它们进行比较:
read highestPOP lowestDG < <(
awk -F ', ' '
FNR == 1 {
dGmin = "" # initialize the min value
POPmax = ""
next
}
NR == 2 || POPmax == "" || $2 > POPmax {POPmax = $2 }
NR == 2 || dGmin == "" || $3 < dGmin {dGmin = $3 }
END {printf "%d %.2f\n", POPmax, dGmin}
' "${storage}"/*_*_*/${str}
)
现在,从技术上讲,它可以工作,但似乎第二个解决方案没有正确报告最小值和最大值。如何正确修复 awk 脚本?
如果您想用于
awk
计算max
/min
跨越一系列文件,只需在命令行上提供这些文件作为awk
脚本的输入(这也可以简单地通过连接所有行来编写为单行,但它的可读性较差。)
让我们分解一行。模式样式是表达式
{
动作}
,任何一部分都是可选的。POP
这里的表达式是在任何ID
非零数字的行上寻找更大的值如果其中至少有一个是,
true
那么我们还需要下一个条件然后...
然后对每个文件中的每一行重复循环。在最后一个文件的末尾
END
执行构造,打印出结果两个值。请注意,只有在比较期间,
awk
循环中的值才会转换为数字。在所有其他时间,它们只是字符串,因此不会损失精度。您可以轻松地将
bash
变量分配给这些输出,从而将不需要的空白作为副作用丢弃对于大量文件,例如 glob 扩展失败,标准
find
方法就足够了,将文件的内容awk
作为STDIN输入,而不是在命令行中列出它们