Các công cụ Linux để coi các tệp là các tập hợp và thực hiện các thao tác thiết lập trên chúng


81

Có ai biết bất kỳ công cụ linux nào được thiết kế đặc biệt để coi các tệp là tập hợp và thực hiện các thao tác thiết lập trên chúng không? Giống như sự khác biệt, giao lộ, vv?

Câu trả lời:


110

Giả sử các phần tử là các chuỗi ký tự không phải là NUL và dòng mới (hãy coi chừng dòng mới là hợp lệ trong tên tệp), bạn có thể biểu diễn một tập hợp dưới dạng tệp văn bản với một thành phần trên mỗi dòng và sử dụng một số tiện ích Unix tiêu chuẩn.

Đặt thành viên

$ 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 }'

Đặt giao lộ

$ 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

Đặt bình đẳng

$ 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

Đặt Cardinality

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

$ awk 'END { print NR }' set

$ sed '$=' set

Kiểm tra tập hợp con

$ 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

Đặt Liên minh

$ 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

Đặt bổ sung

$ 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

Đặt sự khác biệt đối xứng

$ 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

Bộ nguồn

Tất cả các tập hợp con có thể có của một tập hợp không gian hiển thị được phân tách, một không gian trên mỗi dòng:

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

(giả sử các phần tử không chứa SPC, TAB (giả sử giá trị mặc định là $IFS), dấu gạch chéo ngược, ký tự đại diện).

Đặt sản phẩm của Cartesian

$ 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

Kiểm tra tập rời rạc

$ 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

Kiểm tra tập rỗng

$ 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

Tối thiểu

$ 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

Tối đa

$ 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

Tất cả có sẵn tại http://www.catonmat.net/blog/set-operations-in-unix-shell-simplified/


1
Tôi nghĩ rằng phiên bản Python đơn giản và trực quan hơn nhiều. ;-)
Keith

Tôi nghĩ rằng đây là câu trả lời đầy đủ nhất. Thật không may, lệnh nào để chạy hoặc đối số nào (comm -12, -23, -13) trong mỗi trường hợp không phải lúc nào cũng trực quan là "giao nhau" hoặc "khác biệt". Có lẽ sẽ tạo ra một trình bao bọc xung quanh chúng, vì tôi luôn sử dụng những thứ này.
nilton

Tôi đã chạy [pol @ localhost inst] $ grep -xc và INSTALL-BINary 0 [pol @ localhost inst] $ nhưng tôi không hiểu ý nghĩa của nó. Từ "và" sẽ xuất hiện nhiều lần trong tệp. Tôi đang làm gì sai?
Vérace

1
Đặt giao lộ: sort set1 set2 | uniq -dkhông hoạt động cho nhiều bộ. Cân nhắc sử dụng sort <(sort -u set1) <(sort -u set2) | uniq -d.
tân

11

Sắp xếp Bạn cần phải đối phó với việc tự sắp xếp, nhưng commcó thể được sử dụng để làm điều đó, coi mỗi dòng là một thành viên đã đặt: -12cho giao lộ, -13cho sự khác biệt. (Và -23cung cấp cho bạn sự khác biệt lật, nghĩa là, set2 - set1thay vì set1 - set2.) Union nằm sort -utrong thiết lập này.


1
Thật vậy, comm dường như làm hầu hết các công cụ. Mặc dù các lập luận rất không trực quan. Cảm ơn!
nilton

7

Tôi không biết về một công cụ cụ thể nhưng bạn có thể sử dụng Python, và lớp toán tử và toán tử của nó, để viết một tập lệnh nhỏ để làm điều đó.

Đối với exampe:

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

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

Vâng, câu trả lời tốt đẹp. Tại sao sử dụng awk nếu python có sẵn?
guettli

Bạn đã quên:Python> import os
James Bowery

7

Công cụ bàn điều khiển nhỏ bé cài đặt trực tuyến có sẵn trong Debian Stretch và trong Ubuntu kể từ 16.10. Bạn có thể lấy nó qua sudo apt install setop

Dưới đây là một số ví dụ. Các bộ được vận hành trên được cung cấp dưới dạng các tệp đầu vào khác nhau: 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

Các truy vấn Boolean chỉ trả về EXIT_SUCCESStrong trường hợp đúng và EXIT_FAILUREcũng như một thông báo khác. Bằng cách này, setop có thể được sử dụng trong shell. 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)?

Cũng có thể mô tả chính xác cách các luồng đầu vào sẽ được phân tích cú pháp, thực sự bằng các biểu thức thông thường:

  • setop input.txt --input-separator "[[:space:]-]"có nghĩa là khoảng trắng (nghĩa là khoảng trắng \v \t \n \r \f) hoặc dấu trừ được hiểu là dấu phân cách giữa các phần tử (mặc định là dòng mới, tức là mỗi dòng của tệp đầu vào là một phần tử)
  • setop input.txt --input-element "[A-Za-z]+" có nghĩa là các thành phần chỉ là các từ bao gồm các ký tự Latin, tất cả các ký tự khác được coi là dấu phân cách giữa các thành phần

Hơn nữa, bạn có thể

  • --count tất cả các yếu tố của bộ đầu ra,
  • --trim tất cả các yếu tố đầu vào (nghĩa là xóa tất cả các ký tự không mong muốn và thành công trước đó như dấu cách, dấu phẩy, v.v.),
  • coi các phần tử rỗng là hợp lệ thông qua --include-empty,
  • --ignore-case,
  • đặt --output-separatorgiữa các thành phần của luồng đầu ra (mặc định là \n),
  • vân vân

Xem man setophoặc github.com/p4igma/setop để biết thêm thông tin.


3

Nếu bạn thấy một tệp là một tập hợp các dòng và các tệp được sắp xếp, thì có comm.

Nếu bạn thấy một tệp dưới dạng một tập hợp (nhiều) dòng và các dòng không được sắp xếp, grepcó thể tạo ra sự khác biệt và giao nhau (nó đạt được sự khác biệt và giao nhau, nhưng không tôn trọng số lượng cho nhiều trang). Liên minh chỉ là cat.

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

2

Tôi đã tạo một tiện ích Python có thể thực hiện liên kết dòng, giao nhau, khác biệt và sản phẩm của nhiều tệp. Nó được gọi là SetOp, bạn có thể tìm thấy nó trên PyPI ( tại đây ). Cú pháp trông như thế này:

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

1

Tôi đã viết một công cụ nhỏ để làm việc này khá hữu ích với tôi ở nhiều nơi. Giao diện người dùng chưa được chỉnh sửa và tôi không chắc về các đặc tính hiệu suất cho các tệp rất lớn (vì nó đọc toàn bộ danh sách vào bộ nhớ) nhưng "nó hoạt động với tôi". Chương trình có tại https://github.com/nibrahim/lines . Nó ở trong Python. Bạn có thể lấy nó bằng cách sử dụng pip install lines.

Nó hiện hỗ trợ hợp nhất, giao nhau, khác biệt và khác biệt đối xứng của hai tệp. Mỗi dòng của tệp đầu vào được coi là một thành phần của tập hợp.

Nó cũng có hai hoạt động thêm. Một trong những vắt kiệt các dòng trống trong một tệp và thứ hai (rất hữu ích với tôi) là xem qua tệp và chia nó thành các chuỗi tương tự. Tôi cần điều này để tìm các tệp trong danh sách không khớp với mẫu chung.

Tôi hoan nghênh phản hồi.


0

Hệ thống tập tin coi tên tệp (toàn bộ tên tệp, bao gồm cả đường dẫn) là duy nhất.

Hoạt động?

Bạn có thể sao chép các tệp trong a / và b / vào thư mục trống c /, để có được một tập hợp mới.

Với các bài kiểm tra tệp như -e namevòng lặp và tìm hoặc tìm, bạn có thể kiểm tra các tệp hiện có trong hai hoặc nhiều thư mục, để có giao điểm hoặc sự khác biệt.


1
Tôi có nghĩa là coi nội dung của các tệp là các phần tử của một tập hợp (giả sử, một phần tử trên mỗi dòng) và chính các tệp đó là các tập hợp.
nilton

0

Câu trả lời hay nhất ở đây: Setdown (một công cụ chuyên dụng)

Tôi đã viết một chương trình gọi là setdown thực hiện các thao tác Set từ cli.

Nó có thể thực hiện các thao tác thiết lập bằng cách viết một định nghĩa tương tự như những gì bạn sẽ viết trong Makefile:

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

Nó khá mát mẻ và bạn nên kiểm tra nó. Cá nhân tôi không khuyên bạn nên sử dụng các lệnh ad-hoc không được xây dựng cho công việc để thực hiện các thao tác thiết lập. Nó sẽ không hoạt động tốt khi bạn thực sự cần thực hiện nhiều thao tác thiết lập hoặc nếu bạn có bất kỳ thao tác thiết lập nào phụ thuộc vào nhau . Không chỉ vậy mà setdown cho phép bạn viết các hoạt động thiết lập phụ thuộc vào các hoạt động thiết lập khác!

Ở mức độ nào, tôi nghĩ rằng nó khá tuyệt và bạn hoàn toàn nên kiểm tra nó.


0

Mẫu cho nhiều tệp (giao điểm trong trường hợp này):

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

Mở rộng tới:

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

Hồ sơ kiểm tra:

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

Đầu ra:

0
6
12
18

0

Với zshcác mảng ( zshmảng có thể chứa bất kỳ chuỗi byte tùy ý, thậm chí 0).

(cũng lưu ý rằng bạn có thể làm typeset -U arrayđể đảm bảo các yếu tố của nó là duy nhất).

thiết lập thành viên

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

(sử dụng Icờ đăng ký mảng, để lấy chỉ mục của lần xuất hiện cuối cùng $elementtrong mảng (hoặc 0 nếu không tìm thấy). Xóa e(đối với exact) $elementđể được lấy làm mẫu)

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

${array:#pattern}là một biến thể của ksh ${var#pattern}giúp loại bỏ các yếu tố phù hợp với mẫu trái ngược với chỉ loại bỏ phần đầu phù hợp với mẫu. Các (M)(cho phù hợp ) đảo ngược ý nghĩa và loại bỏ tất cả ngoại trừ các yếu tố phù hợp (sử dụng $~elementcho nó được thực hiện như là một mô hình).

đặt giao lộ

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

${set1:*set2}thực hiện giao điểm mảng, nhưng "${(@)...}"cú pháp là cần thiết để bảo toàn các phần tử trống.

thiết lập bình đẳng

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

Kiểm tra xem các mảng có giống nhau không (và theo cùng một thứ tự). Các qcờ mở rộng tham số trích các yếu tố (để tránh các vấn đề với những thứ như a=(1 "2 3")vs b=("1 2" 3)), và (j: :)gia nhập chúng với không gian trước khi làm một so sánh chuỗi.

Để kiểm tra xem chúng có các phần tử giống nhau không, không phân biệt thứ tự, sử dụng ocờ để sắp xếp chúng. Xem thêm ucờ (duy nhất) để loại bỏ trùng lặp.

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

thiết lập cardinality

n=$#array

kiểm tra tập hợp con

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

liên hiệp

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

(xem typeset -Uở trên hoặc ucờ mở rộng tham số để lấy trường hợp trùng lặp). Một lần nữa nếu chuỗi trống không phải là một trong những giá trị có thể, bạn có thể đơn giản hóa thành:

union=($array1 $array2)

bổ sung

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

cho các yếu tố $array1đó không phải là trong $array2.

tối thiểu / tối đa (so sánh từ vựng)

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

tối thiểu / tối đa (so sánh số nguyên thập phân)

min=${${(no)array}[1]} max=${${(no)array}[-1]}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.