我们有两个清单。
更大的“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”,则不会排除它。
- 除了可以想象的散列插入之外,没有进行任何排序,因此要处理的文件可以是您想要的大小,也可以是数据流,等等。
- 在排除开关后传递输入文件名或仅通过管道输入内容。