Linux工具将文件视为集合并对其执行集合操作


Answers:


110

假设元素是NUL和换行符以外的其他字符串(请注意,换行符在文件名中有效),则可以将一个集合表示为文本文件,每行一个元素,并使用一些标准的Unix实用程序。

设置会员

$ grep -Fxc 'element' set   # outputs 1 if element is in set
                            # outputs >1 if set is a multi-set
                            # outputs 0 if element is not in set

$ grep -Fxq 'element' set   # returns 0 (true)  if element is in set
                            # returns 1 (false) if element is not in set

$ awk '$0 == "element" { s=1; exit }; END { exit !s }' set
# returns 0 if element is in set, 1 otherwise.

$ awk -v e='element' '$0 == e { s=1; exit } END { exit !s }'

设置相交

$ comm -12 <(sort set1) <(sort set2)  # outputs intersect of set1 and set2

$ grep -xF -f set1 set2

$ sort set1 set2 | uniq -d

$ join -t <(sort A) <(sort B)

$ awk '!done { a[$0]; next }; $0 in a' set1 done=1 set2

设置平等

$ cmp -s <(sort set1) <(sort set2) # returns 0 if set1 is equal to set2
                                   # returns 1 if set1 != set2

$ cmp -s <(sort -u set1) <(sort -u set2)
# collapses multi-sets into sets and does the same as previous

$ awk '{ if (!($0 in a)) c++; a[$0] }; END{ exit !(c==NR/2) }' set1 set2
# returns 0 if set1 == set2
# returns 1 if set1 != set2

$ awk '{ a[$0] }; END{ exit !(length(a)==NR/2) }' set1 set2
# same as previous, requires >= gnu awk 3.1.5

设置基数

$ wc -l < set     # outputs number of elements in set

$ awk 'END { print NR }' set

$ sed '$=' set

子集测试

$ comm -23 <(sort -u subset) <(sort -u set) | grep -q '^'
# returns true iff subset is not a subset of set (has elements not in set)

$ awk '!done { a[$0]; next }; { if !($0 in a) exit 1 }' set done=1 subset
# returns 0 if subset is a subset of set
# returns 1 if subset is not a subset of set

设置联盟

$ cat set1 set2     # outputs union of set1 and set2
                    # assumes they are disjoint

$ awk 1 set1 set2   # ditto

$ cat set1 set2 ... setn   # union over n sets

$ sort -u set1 set2  # same, but doesn't assume they are disjoint

$ sort set1 set2 | uniq

$ awk '!a[$0]++' set1 set2       # ditto without sorting

设置补码

$ comm -23 <(sort set1) <(sort set2)
# outputs elements in set1 that are not in set2

$ grep -vxF -f set2 set1           # ditto

$ sort set2 set2 set1 | uniq -u    # ditto

$ awk '!done { a[$0]; next }; !($0 in a)' set2 done=1 set1

设置对称差异

$ comm -3 <(sort set1) <(sort set2) | tr -d '\t'  # assumes not tab in sets
# outputs elements that are in set1 or in set2 but not both

$ sort set1 set2 | uniq -u

$ cat <(grep -vxF -f set1 set2) <(grep -vxF -f set2 set1)

$ grep -vxF -f set1 set2; grep -vxF -f set2 set1

$ awk '!done { a[$0]; next }; $0 in a { delete a[$0]; next }; 1;
       END { for (b in a) print b }' set1 done=1 set2

功率设定

一组显示的空间的所有可能子集都是分开的,每行一个:

$ p() { [ "$#" -eq 0 ] && echo || (shift; p "$@") |
        while read r; do printf '%s %s\n%s\n' "$1" "$r" "$r"; done; }
$ p $(cat set)

(假设元素不包含SPC,TAB(假定默认值为$IFS),反斜杠,通配符)。

设置笛卡尔积

$ while IFS= read -r a; do while IFS= read -r b; do echo "$a, $b"; done < set1; done < set2

$ awk '!done { a[$0]; next }; { for (i in a) print i, $0 }' set1 done=1 set2

脱节测试

$ comm -12 <(sort set1) <(sort set2)  # does not output anything if disjoint

$ awk '++seen[$0] == 2 { exit 1 }' set1 set2 # returns 0 if disjoint
                                             # returns 1 if not

空置测试

$ wc -l < set            # outputs 0  if the set is empty
                         # outputs >0 if the set is not empty

$ grep -q '^' set        # returns true (0 exit status) unless set is empty

$ awk '{ exit 1 }' set   # returns true (0 exit status) if set is empty

最低要求

$ sort set | head -n 1   # outputs the minimum (lexically) element in the set

$ awk 'NR == 1 { min = $0 }; $0 < min { min = $0 }; END { print min }'
# ditto, but does numeric comparison when elements are numerical

最大值

$ sort test | tail -n 1    # outputs the maximum element in the set

$ sort -r test | head -n 1

$ awk '$0 > max { max = $0 }; END { print max }'
# ditto, but does numeric comparison when elements are numerical

所有内容均可在http://www.catonmat.net/blog/set-operations-in-unix-shell-simplified/


1
我认为Python版本更加简单直观。;-)
Keith

我认为这是最完整的答案。不幸的是,在每种情况下运行哪个命令或运行哪个参数(comm -12,-23,-13)并不总是直观地表示为“交集”或“差异”。也许会围绕它们创建包装,因为我一直在使用这些东西。
nilton 2011年

我运行了[pol @ localhost inst] $ grep -xc和INSTALL-BINARY 0 [pol @ localhost inst] $,但是我不明白这是什么意思。在文件中,单词“ and”应多次出现。我究竟做错了什么?
Vérace

1
设置交集:sort set1 set2 | uniq -d不适用于多集。考虑使用sort <(sort -u set1) <(sort -u set2) | uniq -d

11

有点。您需要自己进行排序,但是comm可以用来进行排序,将每条线视为集合成员: -12用于相交,-13用于差异。(并-23为您提供了明显的差异,set2 - set1而不是set1 - set2。)sort -u此设置中包含联合。


1
确实,通讯似乎占了大多数。尽管这些论点很不直观。谢谢!
nilton 2011年

7

我不知道特定的工具,但是您可以使用Python及其设置的类和运算符来编写一个小脚本来实现该目的。

例如:

Python> s1 = set(os.listdir("/bin"))
Python> s2 = set(os.listdir("/usr/bin"))
Python> s1 & s2

set(['awk',
     'basename',
     'chroot', ...

是的,很好的答案。如果可用python,为什么要使用awk?
guettli 2015年

您忘记了:Python> import os
James Bowery

7

从16.10开始,Debian Stretch和Ubuntu中都提供了小型控制台工具“ setop”。你可以通过 sudo apt install setop

这里有些例子。将要操作的集合作为不同的输入文件给出: setop input # is equal to "sort input --unique" setop file1 file2 --union # option --union is default and can be omitted setop file1 file2 file3 --intersection # more than two inputs are allowed setop file1 - --symmetric-difference # ndash stands for standard input setop file1 -d file2 # all elements contained in 1 but not 2

布尔查询仅EXIT_SUCCESS在为true时返回,EXIT_FAILURE否则返回一条消息。这样,setop可以在外壳中使用。 setop inputfile --contains "value" # is element value contained in input? setop A.txt B.txt --equal C.txt # union of A and B equal to C? setop bigfile --subset smallfile # analogous --superset setop -i file1 file2 --is-empty # intersection of 1 and 2 empty (disjoint)?

实际上,可以通过正则表达式来精确描述如何解析输入流:

  • setop input.txt --input-separator "[[:space:]-]"表示空格(即\v \t \n \r \f空格)或减号被解释为元素之间的分隔符(默认为换行符,即输入文件的每一行都是一个元素)
  • setop input.txt --input-element "[A-Za-z]+" 表示元素只是由拉丁字符组成的单词,所有其他字符均视为元素之间的分隔符

此外,您可以

  • --count 输出集的所有元素,
  • --trim 所有输入元素(即删除所有不需要的前后字符,例如空格,逗号等),
  • 考虑通过空元素是有效的--include-empty
  • --ignore-case
  • 设置--output-separator输出流的元素之间(默认为\n),
  • 等等。

参见man setopgithub.com/phisigma/setop了解更多信息。


3

如果您将文件视为一组行,并且文件已排序,则为comm

如果您将文件视为一组(多个)行,并且未对行进行排序,则grep可以进行差值和交集(它实现了集差值和交集,但不考虑对多集的计数)。联盟是正义的cat

grep -xF -f small large >intersection
grep -vxF -f small large >difference
cat small large >union

2

我已经制作了一个Python实用程序,可以执行多个文件的逐行合并,相交,差和乘积。它称为SetOp,您可以在PyPI(在此处)上找到它。语法如下所示:

$ setop -i file1 file2 file3  # intersection
$ setop -d file1 file2 file3  # difference

1

我写了一个小工具来做到这一点,这对我在各个地方都非常有用。用户界面未打磨,我不确定大型文件的性能特征(因为它会将整个列表读入内存),但“对我有用”。该程序位于https://github.com/nibrahim/lines。在Python中。您可以使用来获取它pip install lines

目前,它支持两个文件的并集,交集,差异和对称差异。输入文件的每一行都被视为集合的元素。

它还有两个额外的操作。压缩文件中的空白行,第二个(对我来说非常有用)是浏览文件并将其分成相似的字符串集。我需要它来查找列表中与一般模式不匹配的文件。

我欢迎您提供反馈。


0

文件系统将文件名(整个文件名,包括路径)视为唯一。

操作?

您可以将a /和b /中的文件复制到空目录c /中,以获得新的联合集。

使用文件测试(如-e name和循环或查找),您可以检查两个或多个目录中是否存在文件,以获取交集或差异。


1
我的意思是将文件的内容视为集合的元素(假设每行一个元素),并将文件本身视为集合。
nilton

0

最佳答案:放下(专用工具)

我编写了一个名为setdown的程序,该程序从cli执行Set操作。

它可以通过编写类似于您在Makefile中编写的定义来执行设置操作:

someUnion: "file-1.txt" \/ "file-2.txt"
someIntersection: "file-1.txt" /\ "file-2.txt"
someDifference: someUnion - someIntersection

它很酷,您应该检查一下。我个人不建议使用不是为该作业而构建的即席命令来执行设置操作,当您确实需要执行许多设置操作或您有任何相互依赖的设置操作时,它不会很好地工作。不仅如此,setdown还使您可以编写依赖于其他set操作的set操作!

无论如何,我认为这很酷,您应该完全检查一下。


0

多个文件的样本模式(在这种情况下为交集):

eval `perl -le 'print "cat ",join(" | grep -xF -f- ", @ARGV)' t*`

扩展为:

cat t1 | grep -xF -f- t2 | grep -xF -f- t3

测试文件:

seq 0 20 | tee t1; seq 0 2 20 | tee t2; seq 0 3 20 | tee t3

输出:

0
6
12
18

0

对于zsh数组(zsh数组可以包含任意字节序列,甚至0)。

(还请注意,您可以typeset -U array确保其元素是唯一的)。

设定会员

if ((${array[(Ie)$element]})); then
  echo '$element is in $array'
fi

(使用I数组下标标志,以获取数组中最后一次出现的索引$element(如果未找到则为0。删除e(对于exact)$element作为模式)

if ((n = ${(M)#array:#$element})); then
  echo "\$element is found $n times in \$array'
fi

${array:#pattern}是ksh的一种变体,${var#pattern}删除了与模式匹配的元素,而不是仅删除与模式匹配的前导部分。的(M)(用于匹配)反转的含义,并删除所有但匹配元素(使用$~element为它被当作一个图案)。

设置交集

common=("${(@)set1:*set2}")

${set1:*set2}做数组的交集,但是"${(@)...}"需要语法来保留空元素。

设定平等

[[ ${(j: :)${(q)array1}} = ${(j: :)${(q)array2}} ]]

测试数组是否相同(并且顺序相同)。该q参数扩展标记引用的元素(以避免类似事情的问题a=(1 "2 3")VS b=("1 2" 3)),并(j: :)做一个字符串比较之前空间加入他们。

要检查它们具有相同的元素,而与顺序无关,请使用o标志对其进行排序。另请参见u标记(唯一)以删除重复项。

[[ ${(j: :)${(qo)array1}} = ${(j: :)${(qo)array2}} ]]

设定基数

n=$#array

子集测试

if ((${#array1:*array2} == ${#array2})); then
  echo '$array2 is included in $array1'
fi

联盟

union=("$array1[@]" "$array2[@]")

(请参阅typeset -U上面的内容或u参数扩展标志以防重复)。同样,如果空字符串不是可能的值之一,则可以简化为:

union=($array1 $array2)

补充

complement=("${(@)array1:|array2}")

因为那个元素$array1不在$array2

最小/最大(词汇比较)

min=${${(o)array}[1]} max=${${(o)array}[-1]}

最小/最大(十进制整数比较)

min=${${(no)array}[1]} max=${${(no)array}[-1]}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.