awk:在列中找到最小值和最大值

Wan*_*'an 11 bash awk

我正在使用awk处理一个简单的.dat文件,该文件包含几行数据,每行有4列由一个空格分隔.我想找到第一列的最小值和最大值.

数据文件如下所示:

9 30 8.58939 167.759
9 38 1.3709 164.318
10 30 6.69505 169.529
10 31 7.05698 169.425
11 30 6.03872 169.095
11 31 5.5398 167.902
12 30 3.66257 168.689
12 31 9.6747 167.049
4 30 10.7602 169.611
4 31 8.25869 169.637
5 30 7.08504 170.212
5 31 11.5508 168.409
6 31 5.57599 168.903
6 32 6.37579 168.283
7 30 11.8416 168.538
7 31 -2.70843 167.116
8 30 47.1137 126.085
8 31 4.73017 169.496
Run Code Online (Sandbox Code Playgroud)

我使用的命令如下.

min=`awk 'BEGIN{a=1000}{if ($1<a) a=$1 fi} END{print a}' mydata.dat`
max=`awk 'BEGIN{a=   0}{if ($1>a) a=$1 fi} END{print a}' mydata.dat`
Run Code Online (Sandbox Code Playgroud)

但是,输出是min = 10max = 9.

(类似的命令可以返回第二列的正确最小值和最大值.)

有人能告诉我哪里错了吗?谢谢!

Kla*_*uge 11

Awk猜测了这种类型.

字符串"10"小于字符串"4",因为字符"1"在"4"之前.强制进行类型对话,使用零加:

min=`awk 'BEGIN{a=1000}{if ($1<0+a) a=$1} END{print a}' mydata.dat`
max=`awk 'BEGIN{a=   0}{if ($1>0+a) a=$1} END{print a}' mydata.dat`
Run Code Online (Sandbox Code Playgroud)

  • 也值得考虑'awk'NR == 1 {max = $ 1 + 0; next} {if($ 1> max)max = $ 1;} END {print max}'`对于不同的值范围可靠地工作,并确保第一次赋值中`max`是数字.如果文件是任何大小,单次通过计算最小值和最大值将有所帮助. (3认同)

Ed *_*ton 7

你的问题很简单,在你的脚本中你有:

if ($1<a) a=$1 fi
Run Code Online (Sandbox Code Playgroud)

并且 finalfi不是 awk 语法的一部分,因此它被视为变量,a=$1 fi字符串连接也是如此,因此您正在告诉 awka包含字符串,而不是数字,因此字符串比较而不是$1<a.

更重要的是,一般来说,永远不要从一些猜测的最大值/最小值开始,只需使用读取的第一个值作为种子。以下是编写脚本的正确方法:

$ cat tst.awk
BEGIN { min = max = "NaN" }
{
    min = (NR==1 || $1<min ? $1 : min)
    max = (NR==1 || $1>max ? $1 : max)
}
END { print min, max }

$ awk -f tst.awk file
4 12

$ awk -f tst.awk /dev/null
NaN NaN

$ a=( $( awk -f tst.awk file ) )
$ echo "${a[0]}"
4
$ echo "${a[1]}"
12
Run Code Online (Sandbox Code Playgroud)

如果您不喜欢NaN在输入文件为空时选择您喜欢打印的任何内容。


gle*_*man 5

非确定的答案:

cut -d" " -f1 file |
sort -n |
tee >(echo "min=$(head -1)") \
  > >(echo "max=$(tail -1)")
Run Code Online (Sandbox Code Playgroud)

这个开球命令可能有点太聪明了。tee将其stdin流复制到文件名作为参数,再将相同的数据流传输到stdout。我正在使用流程替换来过滤流。

可以使用相同的效果(不那么繁琐)来提取数据流的第一行和最后一行:

cut -d" " -f1 file | sort -n | sed -n '1s/^/min=/p; $s/^/max=/p'
Run Code Online (Sandbox Code Playgroud)

要么

cut -d" " -f1 file | sort -n | { 
    read line
    echo "min=$line"
    while read line; do max=$line; done
    echo "max=$max"
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的解决方案。但我还要指出,“排序”对于仅查找最小值和最大值来说有点矫枉过正。我的文件有超过 100 万行,“sort”的运行速度比其他答案慢,因为它不必要地将所有值按顺序排列。 (2认同)

Hos*_*ani 5

较晚但更短的命令,更精确,无需初始假设:

  awk '(NR==1){Min=$1;Max=$1};(NR>=2){if(Min>$1) Min=$1;if(Max<$1) Max=$1} END {printf "The Min is %d ,Max is %d",Min,Max}' FileName.dat
Run Code Online (Sandbox Code Playgroud)