如何在Unix终端中查找唯一且连续列表(每行一个)中缺失的整数?

16
假设我有一个文件,内容如下(按行排列的整数排序列表,每行一个数字):
1
3
4
5
8
9
10

我希望得到以下输出(即列表中缺失的整数):

2
6
7

如何在Bash终端中使用awk或类似的解决方案(最好是一行代码)来完成此操作?

6个回答

28

使用 awk 命令可以做到这一点:

awk '{for(i=p+1; i<$1; i++) print i} {p=$1}' file

2
6
7

说明:

  • {p = $1}:变量p包含前一个记录的值
  • {for ...}:我们循环从p+1到当前行的值(不包括当前值),并打印每个值,这些值基本上是缺失的值

3
变量p包含来自上一条记录的值,我们从p+1循环到当前行的值。 - anubhava
1
为什么这个答案没有排在最高评分和被采纳的答案之上? - Gaurav Kansal
1
@GauravKansal:点击问题右下角的“最旧”或“投票”排序选项。 - anubhava
如果序列不是以1开始,而是以文件中的第一个值为开头呢?下面的seq/grep答案确实适用于我的情况。 - xref
1
对文件中的第一行进行轻微修改以进行不同处理:NR == 1 { last_number = $1 } ; NR > 1 { for (i = last_number + 1; i < $1; i++) print i; last_number = $1 } - undefined
显示剩余2条评论

8

使用 seqgrep:

seq $(head -n1 file) $(tail -n1 file) | grep -vwFf file -

seq创建完整的序列,grep从中删除文件中存在的行。


我个人认为“-”不必要,它存在的原因是什么? - Hashim Aziz
不是必需的,它意味着标准输入,这也是默认值。 - choroba

2
perl -nE 'say for $a+1 .. $_-1; $a=$_'

1
为了适应choroba的聪明答案,以满足我的使用情况,我需要让我的序列处理零填充数字。
这里的魔法是seq-w开关-它自动用必要数量的零填充第一个数字,以使其与第二个数字对齐。
-w, --equal-width     equalize width by padding with leading zeroes

我的整数范围从0到9999,所以我使用了以下代码:
seq -w 0 9999 | grep -vwFf "file.txt"

这个程序可以找到从00009999序列中缺失的整数。或者换句话说,按照choroba回答的更通用的解决方案:

seq -w $(head -n1 "file.txt") $(tail -n1 "file.txt") | grep -vwFf "file.txt"

我个人认为他的回答中的-并不必要,但可能有一些使用情况需要它。

1

如果filein包含数字列表,则不调用任何外部程序:

#!/bin/bash
i=0
while read num; do
    while (( ++i<num )); do
        echo $i
    done
done <filein

1
使用 Raku(前身为 Perl 6)
raku -e 'my @a = lines.map: *.Int; say @a.Set (^) @a.minmax.Set;' 

输入示例:

1
3
4
5
8
9
10

样例输出:

Set(2 6 7)

我相信有一种类似于 @JJoao 聪明的 Perl5 解决方案的 Raku 解决方案,但是在思考这个问题时,我的思维自然而然地转向了 Set 操作。

上面的代码将 lines 读入到 @a 数组中,并将每行映射为 @a 数组中的元素为 Int,而不是字符串。在第二个语句中,@a.Set 将数组转换为左侧的 Set,并使用 (^) 运算符。同样在第二个语句中,@a.minmax.Set 将数组转换为第二个 Set,位于 (^) 运算符的右侧,但是这次因为使用了 minmax 运算符,所有从 minmaxInt 元素都包括在内。最后,(^) 符号是对称差(中缀)运算符,用于查找差异。

要获取缺失整数的无序空格分隔列表,请将上述的 say 替换为 put。要获取连续排序的缺失整数列表,请在下面添加显式的 sort

~$ raku -e 'my @a = lines.map: *.Int; .put for (@a.Set (^) @a.minmax.Set).sort.map: *.key;' file
2
6
7

以上所有Raku代码的优点在于,查找“缺失整数”不需要“顺序列表”作为输入,输入也不需要是唯一的。因此,希望这段代码除了在问题中明确说明的情况下,还能对各种问题有用。
另一方面,Raku是一种Perl家族语言,所以TMTOWTDI。下面创建一个@a.minmax数组,并进行grep,以便返回@a元素中的nonenone连接符):
~$ raku -e 'my @a = lines.map: *.Int;  .put for @a.minmax.grep: none @a;'  file
2
6
7

https://docs.raku.org/language/setbagmix
https://docs.raku.org/type/Junction
https://raku.org


1
第一次了解 raku ++ - anubhava
1
@anubhava:“Raku(前身为Perl 6)是Perl语言家族的姊妹语言,不是Perl的替代品,而是一种独立的语言。存在库使您能够从Raku程序中调用Perl代码,反之亦然。” 引自:https://www.perl.org/ - jubilatious1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接