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

nil*_*ton 91 command-line utilities text-processing

有谁知道任何专门用于将文件视为集合并对它们执行集合操作的 Linux 工具?像差异,交集等?

llh*_*uii 119

假设元素是除 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 }'
Run Code Online (Sandbox Code Playgroud)

设置交点

$ 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
Run Code Online (Sandbox Code Playgroud)

设置平等

$ 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
Run Code Online (Sandbox Code Playgroud)

设置基数

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

$ awk 'END { print NR }' set

$ sed '$=' set
Run Code Online (Sandbox Code Playgroud)

子集测试

$ 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
Run Code Online (Sandbox Code Playgroud)

设置联合

$ 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
Run Code Online (Sandbox Code Playgroud)

设置补码

$ 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
Run Code Online (Sandbox Code Playgroud)

设置对称差

$ 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
Run Code Online (Sandbox Code Playgroud)

电源组

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

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

(假设元素不包含 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
Run Code Online (Sandbox Code Playgroud)

不相交集测试

$ 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
Run Code Online (Sandbox Code Playgroud)

空集测试

$ 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
Run Code Online (Sandbox Code Playgroud)

最低限度

$ 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
Run Code Online (Sandbox Code Playgroud)

最大值

$ 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
Run Code Online (Sandbox Code Playgroud)

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


gee*_*aur 14

有点。您需要自己处理排序,但comm可以用来做到这一点,将每条线视为一个集合成员: -12用于交集,-13用于差异。(并-23为您提供翻转差异,即,set2 - set1而不是set1 - set2。)联合sort -u在此设置中。

  • 事实上,comm 似乎可以完成大部分工作。尽管这些论点非常不直观。谢谢! (2认同)

Fra*_*ank 10

自 16.10 起,小型控制台工具“setop”现在可用于 Debian Stretch 和 Ubuntu。你可以通过 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在为真时返回,EXIT_FAILURE否则返回消息。这样,就可以在shell 中使用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


Kei*_*ith 8

我不知道有什么特定的工具,但您可以使用 Python 及其集合类和运算符来编写一个小脚本来执行此操作。

例如:

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

set(['awk',
     'basename',
     'chroot', ...
Run Code Online (Sandbox Code Playgroud)