Bash:由行号和列寻址的算术

时间:2017-02-03 18:32:36

标签: bash awk

我通常使用Excel完成此操作,但在我尝试学习bash时,我想在此处征求有关如何操作的建议。我的输入文件类似于:

@       s0      legend  "1001"
@       s1      legend  "1002"
@target G0.S0
@type xy
2.0     -1052.7396157664
2.5     -1052.7330560932
3.0     -1052.7540013664
3.5     -1052.7780321236
4.0     -1052.7948229060
4.5     -1052.8081313831
5.0     -1052.8190310613
&
@target G0.S1
@type xy
2.0     -1052.5384564253
2.5     -1052.7040374678
3.0     -1052.7542803612
3.5     -1052.7781686744
4.0     -1052.7948927247
4.5     -1052.8081704241
5.0     -1052.8190543049
&

其中上面只显示两个数据集:s0和s1。实际上我有17个数据集并将它们任意组合。通过组合,我的意思是我想:

  1. 对于两个数据集,分别提取每个数据集的第二列。
  2. 逐行减去这两列。
  3. 将差值乘以常数$ C.
  4.   

    注意:$ C乘以非常小的数字,唯一可以让它除以零的方法就是大规模。

    编辑:在提出请求后,我显然不完全清楚自己的目标。举个例子:

    set0
    2   x
    3   y
    4   z
    set1
    2   r
    3   s
    4   t
    

    我也定义了一个常数C。

    我想执行以下操作:

    C*(r - x)
    C*(s - y)
    C*(t - z)
    

    我将为套装>做这个1,最多16,例如(设置10)减(设置0)。因此,我需要灵活地根据其行号和列号来定位值,并且最好在一系列行号上起作用,以使其有效。

    到目前为止,这有效:

    C=$(echo "scale=45;x=(small numbers)*(small numbers); x" | bc -l)
    
    sed -n '5,11p' input.in | cut -c 5-20 > tmp1.in
    sed -n '15,21p' input.in | cut -c 5-20 > tmp2.in
    pr -m -t -s tmp1.in tmp2.in > tmp3.in
    awk '{printf $2-$1 "\n"}' tmp3.in > tmp4.in
    

    但乘法失败:

    awk '{printf "%11.2f\n", "$C"*$1 }' tmp4.in > tmp5.in
    

    返回:

           0.00
           0.00
           0.00
           0.00
           0.00
           0.00
           0.00
    

    我觉得用awk可以更加优雅地完成整个事情。我也试过这个:

    for (( i=0; i<=6; i++ ))
    do
    
    n=5+$i
    m=10+n
    
    awk 'NR==n{a=$2};NR==m{b=$2} {printf "%d\n", $b-$a}' input.in > temp.in
    done
    

    但我得到的所有temp.in都是0列的长列。

    我也试过

    awk 'NR==5,NR==11{a=$2};NR==15,NR==21{b=$2} {printf "%d\n", $b-$a}' input.in > temp.in
    

    但得到了错误

    awk: (FILENAME=input.in FNR=20) fatal: attempt to access field -1052
    

    任何想法如何用awk表达,如果那不起作用,那么为什么我不能用上面的awk乘法呢?谢谢!

3 个答案:

答案 0 :(得分:1)

这可以一次性完成数学

$ awk -v c=1 '/^&/ {s++} 
              s==1 {a[$1]=$2} 
              s==3 {print $1,a[$1],$2,c*(a[$1]-$2)} 
           /@type/ {s++}' file

2.0 -1052.7396157664 -1052.5384564253 -0.201159
2.5 -1052.7330560932 -1052.7040374678 -0.0290186
3.0 -1052.7540013664 -1052.7542803612 0.000278995
3.5 -1052.7780321236 -1052.7781686744 0.000136551
4.0 -1052.7948229060 -1052.7948927247 6.98187e-05
4.5 -1052.8081313831 -1052.8081704241 3.9041e-05
5.0 -1052.8190310613 -1052.8190543049 2.32436e-05

您可以轻松删除装饰并添加打印格式。幻数1=g13=2*g2-1对应于数据组1和2,因为数据文件中显示的顺序也可以转换为awk变量。

计数器s会跟踪您是否在一组中,奇数对应于集合之间的集合和偶数。增量在开始模式和结束模式下完成。增量语句的顺序是这样设置的,它们不会在模式之后打印(首先取消设置,打印设置值,最后重置}。您可以更改顺序并观察效果。

答案 1 :(得分:0)

在第一次尝试中,您应该替换该行:

awk '{printf "%11.2f\n", "$C"*$1 }' tmp4.in > tmp5.in

与那一个:

awk -v C=$C '{printf "%11.2f\n", C*$1 }' tmp4.in > tmp5.in

您正在使用带有awk的符号混合bash shell的符号。

  • 在shell中定义不带$的变量,并将其与$一起使用。
  • 这里你是awk脚本,没有$来使用变量。然而,有一些特殊的变量:$ 1 $ 2 ......
  • 你已经把单引号&#39;你的awk脚本周围,所以shell变量不能使用。我的意思是你已经写了$C,但shell无法在单引号中看到它。这就是为什么你必须编写awk -v C=$C以便将shell变量$C转移到名为C的awk变量。

在你使用awk的其他尝试中,我们也可以看到这样的错误。现在我想你会成功。

答案 2 :(得分:0)

这可能就是你要找的东西:

$ cat tst.awk
/^[@&]/ { lineNr=0; next }
{
    ++lineNr
    if (lineNr in prev) {
        print $1, c * ($2 - prev[lineNr])
    }
    prev[lineNr] = $2
}

$ awk -v c=100000 -f tst.awk file
2.0 20115.9
2.5 2901.86
3.0 -27.8995
3.5 -13.6551
4.0 -6.98187
4.5 -3.9041
5.0 -2.32436