通过命令行检查一列中的数字是否连续

时间:2019-09-09 22:04:39

标签: bash awk command-line number-formatting string-parsing

在文本文件中,一列中有一个数字序列,后跟一个短字符串。这是示例文件中“名称”下的第5列:

SESSION NAME:   session
SAMPLE RATE:    48000.000000
BIT DEPTH:  16-bit
SESSION START TIMECODE: 00:00:00:00.00
TIMECODE FORMAT:    24 Frame
# OF AUDIO TRACKS:  2
# OF AUDIO CLIPS:   2
# OF AUDIO FILES:   2


M A R K E R S  L I S T I N G
#       LOCATION        TIME REFERENCE      UNITS       NAME                                COMMENTS
2       0:00.500        24000               Samples     xxxx0001                            
3       0:03.541        170000              Samples     xxxx0002                            
4       0:05.863        281458              Samples     xxxx0003                            
5       0:08.925        428430              Samples     xxxx0004                            
6       0:10.604        509025              Samples     xxxx0005                            
7       0:13.973        670742              Samples     xxxx0006                            
8       0:15.592        748453              Samples     xxxx0008                            
9       0:19.243        923666              Samples     xxxx0008


在上面的示例中,缺少0007,并且复制了0008。

因此,我希望能够检查数字是否为

  
      
  1. 给定列中当前存在的范围。
  2.   
  3. 是否有重复项
  4.   

我还要输出以下结果:

SKIPPED:
xxxx0007

DUPLICATES:
xxxx0008

我能获得的最远的结果是使用awk来获得我需要的列:

cat <file.txt> | awk '{ print $5 }'

这让我明白了

NAME
xxxx0001
xxxx0002
xxxx0003
xxxx0004
xxxx0005
xxxx0006
xxxx0008
xxxx0008

但是我不知道从这里去哪里。

我是否需要遍历列表项并进行解析,以便仅获取数字,然后开始对下一行进行一些比较?

任何帮助将不胜感激 谢谢!

1 个答案:

答案 0 :(得分:2)

作为起点,请尝试以下操作:

awk '
NR>1 { gsub("[^0-9]", "", $5); count[$5]++ }
END {
    print "Skipped:"
    for (i=1; i<NR; i++)
        if (count[i] == 0) printf "xxxx%04d\n", i
    print "Duplicates:"
    for (i=1; i<NR; i++)
        if (count[i] > 1) printf "xxxx%04d\n", i
} ' file.txt

输出:

Skipped:
xxxx0007
Duplicates:
xxxx0008
  • 条件NR>1用于跳过顶部标题行。
  • gsub("[^0-9]", "", $5)$5中删除非数字字符。 结果,$5被设置为从第5列提取的数字。
  • 数组count[]计算每个数字的出现次数。如果值 是0(或未定义),表示该数字被跳过。如果值 大于1,则数字重复。
  • END { ... }块在所有输入行处理完毕后 执行 并报告最终结果非常有用。

但是,“跳过/重复”方法不能很好地检测到以下情况:

#       LOCATION        TIME REFERENCE      UNITS       NAME            COMMENTS
1       0:00.500        24000               Samples     xxxx0001
2       0:02.888        138652              Samples     xxxx0003
3       0:04.759        228446              Samples     xxxx0004
4       0:07.050        338446              Samples     xxxx0005
5       0:09.034        433672              Samples     xxxx0006
6       0:12.061        578958              Samples     xxxx0007
7       0:14.111        677333              Samples     xxxx0008
8       0:17.253        828181              Samples     xxxx0009

#       LOCATION        TIME REFERENCE      UNITS       NAME            COMMENTS
1       0:00.500        24000               Samples     xxxx0001
2       0:02.888        138652              Samples     xxxx0003
3       0:04.759        228446              Samples     xxxx0002
4       0:07.050        338446              Samples     xxxx0004
5       0:09.034        433672              Samples     xxxx0005
6       0:12.061        578958              Samples     xxxx0006
7       0:14.111        677333              Samples     xxxx0007
8       0:17.253        828181              Samples     xxxx0008

最好在期望值和实际值之间进行逐行比较。那怎么样:

awk '
NR>1 {
    gsub("[^0-9]", "", $5)
    if ($5 != NR-1) printf "Line: %d  Expected: xxxx%04d  Actual: xxxx%04d\n", NR, NR-1, $5
} ' file.txt

原始示例的输出:

Line: 8  Expected: xxxx0007  Actual: xxxx0008

[编辑]

根据修订后的输入文件(其中包含更多额外的标题行),如何操作:

awk '
f {
    gsub("[^0-9]", "", $5)
    if ($5 != NR-skip) printf "Line: %d  Expected: xxxx%04d  Actual: xxxx%04d\n", NR, NR-skip, $5
}
/^#[[:blank:]]+LOCATION[[:blank:]]+TIME REFERENCE/ {
    skip = NR
    f = 1
}
' file.txt

输出:

Line: 19  Expected: xxxx0007  Actual: xxxx0008

上面的脚本跳过这些行,直到找到特定的模式# LOCATION TIME REFERENCE

  • 如果f { ... }为true,则执行f块。因此跳过该块 直到f设置为非零值。
  • 如果输入行与/^# .../ { ... }块匹配, 图案。如果找到skip,则将其设置为标题行的数量,并且 f(标志)设置为1,因此从下一个执行上一个块 迭代。

希望这会有所帮助。