awk for 循环没有将数组索引设置为正确的值

awk for loop does not set array index to correct value

我有这个小型地理位置数据集。

37.9636140,23.7261360
37.9440840,23.7001760
37.9637190,23.7258230
37.9901450,23.7298770

来自随机位置。 例如这个 37.97570, 23.66721 我需要用 awk 创建一个 bash 命令,returns 距离具有简单的欧氏距离。 这是我使用的命令

awk -v OFMT=%.17g -F',' -v long=37.97570 -v lat=23.66721 '{for (i=1;i<=NR;i++) distances[i]=sqrt(( - long)^2 + ( - lat)^2 ); a[i]=; b[i]=} END {for (i in distances) print distances[i], a[i], b[i]}' filename

当我 运行 这个命令时,我得到了这个不正确的奇怪结果,有人可以向我解释我做错了什么吗?

➜ awk -v OFMT=%.17g -F',' -v long=37.97570 -v lat=23.66721 '{for (i=1;i<=NR;i++) distances[i]=sqrt(( - long)^2 + ( - lat)^2 ); a[i]=; b[i]=} END {for (i in distances) print distances[i], a[i], b[i]}' filename                     

44,746962127881936 37.9440840 23.7001760
44,746962127881936 37.9901450 23.7298770
44,746962127881936 37.9636140 23.7261360
44,746962127881936  
44,746962127881936 37.9637190 23.7258230

已更新。

附加了@jas 提供的命令,我将 od -c 作为@mark-fuso 的建议包含在内。

The issue now is that I get different results from @jas

展示新问题的命令输出。

awk -v OFMT=%.17g -F, -v long=37.97570 -v lat=23.66721 '
{distance=sqrt(( - long)^2 + ( - lat)^2 ); print distance, , }
' file        
1,1820150904705098 37.9636140 23.7261360
1,1820150904705098 37.9440840 23.7001760
1,1820150904705098 37.9637190 23.7258230
1,1820150904705098 37.9901450 23.7298770

od -c that shows the content of the input file.

od -c file
0000000   3   7   .   9   6   3   6   1   4   0   ,   2   3   .   7   2
0000020   6   1   3   6   0  \n   3   7   .   9   4   4   0   8   4   0
0000040   ,   2   3   .   7   0   0   1   7   6   0  \n   3   7   .   9
0000060   6   3   7   1   9   0   ,   2   3   .   7   2   5   8   2   3
0000100   0  \n   3   7   .   9   9   0   1   4   5   0   ,   2   3   .
0000120   7   2   9   8   7   7   0  \n
0000130

Awk 会为您处理循环。输入文件的每一行代码将依次运行:

$ awk -v OFMT=%.17g -F, -v long=37.97570 -v lat=23.66721 '
{distance=sqrt(( - long)^2 + ( - lat)^2 ); print distance, , }
' file
0.060152679674309095 37.9636140 23.7261360
0.045676346307474212 37.9440840 23.7001760
0.059824979147508742 37.9637190 23.7258230
0.064310270672728084 37.9901450 23.7298770

编辑: OP 得到不同的结果。我注意到在 OP 的输出中打印 distance 时有逗号而不是小数点。这表明区域设置可能存在问题。

OP 确认语言环境设置为希腊语,导致输出不同。

虽然@jas 已经为这个问题提供了 'fix',但我想我会就 OP 的代码正在做什么发表一些评论...

一些基础知识...

  • awk 程序 ({for (i=1;i<=NR;i++) ... ; b[i]=}) 应用于输入文件的每一行
  • 随着从输入文件中读取每一行,awk 变量 NR 跟踪行号(即,第一行 NR=1NR=2对于第二行,等等)
  • 在最后一次通过 for 循环时,计数器(在本例中为 i)的值为 NR+1(即应用 i++在最后一次通过循环时留下 i=NR+1)
  • 除非对每一行输入进行条件检查,否则 awk 程序将对输入文件中的每一行应用(包括空行 - 下文详述)
  • for (i in distances)... 不保证按数字顺序处理数组索引

awk/for 循环正在执行以下操作:

  • 对于第一个输入行 (NR=1),我们得到 for (i=1;i<=1;i++) ...
  • 对于第二个输入行 (NR=2),我们得到 for (i=1;i<=2;i++) ...
  • 对于第 3 个输入行 (NR=3),我们得到 for (i=1;i<=3;i++) ...
  • 对于第 4 个输入行 (NR=4),我们得到 for (i=1;i<=4;i++) ...

对于 awk 处理的每一行,程序将覆盖 distance[] 数组中的所有先前条目;最终结果是最后一行 (NR=4) 将在 distance[] 数组的所有 4 个条目中放置相同的值。

a[i]=; b[i]= 数组赋值发生在 for 循环范围之外,因此每个输入行将分配一次(即,不会被覆盖)但是,数组赋值正在进行用 i=NR+1;最终结果是第一行 (NR=1) 的内容存储在数组条目 a[2]b[2] 中,第二行 (NR=2) 的内容存储在数组中条目 a[3]a[3],等等

针对我得到的 4 行输入文件,使用 print i, distances[i], a[i], b[i]} 和 运行 修改 OP 的代码:

1 0.064310270672728084                            # no data for 2nd/3rd columns because a[1] and b[1] are never set
2 0.064310270672728084 37.9636140 23.7261360      # 2nd/3rd columns are from 1st row of input
3 0.064310270672728084 37.9440840 23.7001760      # 2nd/3rd columns are from 2nd row of input
4 0.064310270672728084 37.9637190 23.7258230      # 2nd/3rd columns are from 3rd row of input

从这里我们可以看到输出的第一列是相同的(即distance[1]=distance[2]=distance[3]=distance[4]),而第二列和第三列与输入列相同除了 它们移动 'down' 一行。

这给我们留下了两个悬而未决的问题......

  • 为什么OP显示5行输出?
  • 为什么第一列是垃圾44,746962127881936

我能够通过在我的输入文件末尾添加一个空行来重现这个问题:

$ cat geo.dat
37.9636140,23.7261360
37.9440840,23.7001760
37.9637190,23.7258230
37.9901450,23.7298770
                           <<=== blank line !!

使用 OP 的 awk 代码生成以下内容:

44.746962127881936
44.746962127881936 37.9636140 23.7261360
44.746962127881936 37.9440840 23.7001760
44.746962127881936 37.9637190 23.7258230
44.746962127881936 37.9901450 23.7298770

注释:

  • 此顺序与 OP 的示例输出不同,可能是由于 OP 的 awk 版本未按数字顺序处理 for (i in distances)...; OP 可以尝试 for (i=1;i<=NR;i++)...for (i=1;i in distances; i++)... 之类的东西(尽管后者对于人口稀少的数组无法正常工作)
  • OPs 输出(在问题中;在对@jas 的回答的评论中)显示逗号 (,) 代替第一列的句点 (.),所以我猜测 OP 的环境正在使用将 comma/period 切换为 thousands/decimal 定界符的语言环境(尽管输入数据基于 'opposite' 语言环境)

注意我们终于看到了第 4 行输入的数据(移动 'down' 并显示在第 5 行)但是第一列似乎是一个无意义的值......这可能是追溯到对空白行应用以下内容:

sqrt(( - long)^2     + ( - lat)^2     )
sqrt((   - long)^2     + (   - lat)^2     )  # empty line =>  =  = undefined/empty
sqrt((   - 37.97570)^2 + (   - 23.66721^2 )  
sqrt( 1442.153790      +    560.136829    )
sqrt( 2002.290619                         )
44.746952...                                 # contents of 1st column 

为了 'fix' 这个问题,OP 可以 a) 从输入文件中删除空行或 b)awk 脚本添加一些逻辑,以便仅在输入行在字段 #1 和 #2 中具有(数字)值时才执行计算(即,</code> 和 <code> 不为空);由编码人员决定应用多少验证(例如,字段是否为数字,字段是否在合法 long/lat 值的范围内,等等)。


最后一个与设计相关的评论...如 jas 的回答所示,当处理时可以生成所有所需的输出时,不需要任何数组(这反过来减少了内存使用)'on-the-fly'输入文件的每一行。