我们需要一个“C”列表,其中包含“A”的所有元素,但不包含“B”的任何元素

我们需要一个“C”列表,其中包含“A”的所有元素,但不包含“B”的任何元素

我们有两个清单。

更大的“A”:

A=`echo -e '1\n2\n3\n4\n5'`
echo "$A"
1
2
3
4
5

和一个较小的“B”:

B=`echo -e '1\n2\n3'`
echo "$B"
1
2
3

问:但是我们需要第三个列表,其中包含“A”的所有元素,但没有任何“B”,我如何在 bash 中做到这一点?

echo "$C"
4
5

数字可以是任何数字,从“foo”到 99 等等。

更新:

它可以在 shell 中手动运行,但很奇怪,因为如果我把它放在脚本中,它就不起作用!

cat a.txt 
A=$(seq 5)
B=$(seq 3)
comm -23 <(sort <<< "$A") <(sort <<< "$B")
sh a.txt 
a.txt: line 3: syntax error near unexpected token `('
a.txt: line 3: `comm -23 <(sort <<< "$A") <(sort <<< "$B")'

手工做它的作品..:

A=$(seq 5)
B=$(seq 3)
comm -23 <(sort <<< "$A") <(sort <<< "$B")
4
5

为什么?更新更新:需要使用 bash 而不是“sh”:D

答案1

comm命令是你所需要的:

$ A=$(seq 5)
$ B=$(seq 3)
$ comm -23 <(sort <<< "$A") <(sort <<< "$B")
4
5

这是一种不需要对输入进行排序的方法。这是 awk 中的常见习惯用法,将第一个文件读入内存,然后根据第一个文件对第二个文件进行一些过滤。让我们尝试使用随机数据

$ A=$(seq 5 | sort -R); echo "$A"
3
5
1
2
4
$ B=$(seq 3 | sort -R); echo "$B"
2
1
3

我们期望输出先是 5,然后是 4:

$ awk 'NR==FNR {b[$1]=1; next} !($1 in b) {print}' <(echo "$B") <(echo "$A")
5
4

答案2

正如格伦·杰克曼所提供的,该comm实用程序是执行此操作的最简单方法。然而该方法破坏了排序顺序。

还有另一种方法可以实现此目的,即保留原始排序顺序(尽管两个列表必须以相同的顺序预先排序):

diff --unchanged-line-format '' --old-line-format '' file_a file_b

file_b这将返回原始顺序中唯一的所有行。
 

我相信如果数据集也非常大,这也会更有效。因为排序操作可能会很昂贵。但这只是一个猜测。

答案3

sort a b b | uniq -u

比山丘(UNIX 7)更古老,但仍然有效。

答案4

或者,你知道,Perl:

#!/usr/bin/perl -s
if($#ARGS == 0) {print "Usage: $0 -exclude=fileWithLinesToExclude [inputFile]\n"; exit(0)}
open(EXCL, $exclude);
%excluded = map { $_ => 1 } <EXCL>;
while(<>) {
   print $_ unless $excluded{$_};
} 

事物

  • perl -s允许开关变为变量值
  • 没有咀嚼的情况发生;如果排除行是“foobar_”并且处理后的行是“foobar”,则不会排除它。
  • 除了可以想象的散列插入之外,没有进行任何排序,因此要处理的文件可以是您想要的大小,也可以是数据流,等等。
  • 在排除开关后传递输入文件名或仅通过管道输入内容。

相关内容