bash,找到最接近的下一个值,前进和后退

时间:2014-05-23 06:39:28

标签: bash awk find asort

我有一个data.txt文件

1    2     3    4      5       6        7   
cat data.txt
13 245 1323 10.1111 10.2222 60.1111 60.22222
13 133 2325 11.2222 11.333  61.2222 61.3333
13 245 1323 12.3333 12.4444 62.3333 62.44444444
13 245 1323 13.4444 13.5555 63.4444 63.5555

查找下一个最接近的内容:我的目标值为11.6667,它应该在column 4中找到最近的下一个值12.3333

查找上一个最近的内容:我的目标值为62.9997,它应该在column 6中找到最近的前一个值62.3333

我能够通过

找到下一个最近的(案例1)
awk -v c=4 -v t=11.6667 '{a[NR]=$c}END{
        asort(a);d=a[NR]-t;d=d<0?-d:d;v = a[NR]
        for(i=NR-1;i>=1;i--){
                m=a[i]-t;m=m<0?-m:m
                if(m<d){
                    d=m;v=a[i]
                }
        }
        print v
}' f
12.3333

任何bash解决方案?找到最近的(案例2)?

4 个答案:

答案 0 :(得分:1)

第一部分:

awk -v v1="11.6667" '$4>v1 {print $4;exit}' file
12.3333

第二部分:

awk -v v2="62.9997" '$6>v2 {print p;exit} {p=$6}' file
62.3333

一气呵成:

awk -v v1="11.6667" -v v2="62.9997" '$4>v1 && !p1 {p1=$4} $6>v2 && !p2 {p2=p} {p=$6} END {print p1,p2}' file
12.3333 62.3333

答案 1 :(得分:1)

试试这个:

$ cat tst.awk
{
    if ($fld > tgt) {
        del = $fld - tgt
        if ( (del < minGtDel) || (++gtHit == 1) ) {
            minGtDel = del
            minGtVal = $fld
        }
    }
    else if ($fld < tgt) {
        del = tgt - $fld
        if ( (del < minLtDel) || (++ltHit == 1) ) {
            minLtDel = del
            minLtVal = $fld
        }
    }
    else {
        minEqVal = $fld
    }
}
END {
    print (minGtVal == "" ? "NaN" : minGtVal)
    print (minLtVal == "" ? "NaN" : minLtVal)
    print (minEqVal == "" ? "NaN" : minEqVal)
}

$ awk -v fld=4 -v tgt=11.6667 -f tst.awk file
12.3333
11.2222
NaN

$ awk -v fld=6 -v tgt=62.9997 -f tst.awk file
63.4444
62.3333
NaN

$ awk -v fld=6 -v tgt=62.3333 -f tst.awk file
63.4444
61.2222
62.3333

答案 2 :(得分:0)

您的解决方案看起来不必要地复杂化(存储整个数组并对其进行排序),如果您重新考虑bash,我认为您会看到awk解决方案。

awk中,您可以使用

检测第一行
FNR==1 {do something}

所以在第一行,将变量BestYet设置为您要搜索的列中的值。

在后续行中,只需测试您正在检查的列中的值是否为

a) less than your target AND 
b) greater than `BestYet`

如果是,请更新BestYet。最后,打印BestYet

bash中,应用相同的逻辑,但将每行读入bash数组并使用${a[n]}来获取第n个元素。

答案 3 :(得分:0)

我不知道这是不是你想要的,但这就是我想出来的,不知道awk

#!/bin/sh

IFSBAK=$IFS
IFS=$'\n'

best=

for line in `cat $1`; do
    IFS=$' \t'
    arr=($line)


    num=${arr[5]}
    [[ -z $best ]] && best=$num

    if [ $(bc <<< "$num < 62.997") -eq 1 ]; then 
        if [  $(bc <<< "$best < $num") -eq 1 ]; then
            best=$num
        fi
    fi

    IFS=$'\n'
done

IFS=$IFSBAK
echo $best

如果需要,可以添加列和输入值62.997作为参数,我没有证明它会特意查找您想要的内容。

编辑以删除文件已排序的假设。