Alat Linux untuk memperlakukan file sebagai set dan melakukan operasi set pada mereka

82

Apakah ada yang tahu tentang alat linux yang dirancang khusus untuk memperlakukan file sebagai set dan melakukan operasi set pada mereka? Suka perbedaan, persimpangan, dll?

nilton
sumber

Jawaban:

110

Dengan asumsi elemen adalah rangkaian karakter selain NUL dan baris baru (berhati-hatilah bahwa baris baru itu valid dalam nama file), Anda dapat mewakili satu set sebagai file teks dengan satu elemen per baris dan menggunakan beberapa utilitas Unix standar.

Tetapkan Keanggotaan

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

Atur titik potong

$ 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

Tetapkan Kesetaraan

$ 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

Atur Kardinalitas

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

$ awk 'END { print NR }' set

$ sed '$=' set

Tes Subset

$ 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

Atur Serikat

$ 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

Setel Komplemen

$ 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

Setel Perbedaan Simetris

$ 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

Set Daya

Semua himpunan bagian dari himpunan ditampilkan ditampilkan terpisah, satu per baris:

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

(mengasumsikan elemen tidak mengandung SPC, TAB (dengan asumsi nilai default $IFS), backslash, karakter wildcard).

Setel Produk 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

Disjoint Set Test

$ 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

Tes Kosong

$ 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

Minimum

$ 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

Maksimum

$ 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

Semua tersedia di http://www.catonmat.net/blog/set-operations-in-unix-shell-simplified/

llhuii
sumber
1
Saya pikir versi Python jauh lebih sederhana dan lebih intuitif. ;-)
Keith
Saya pikir ini adalah jawaban yang paling lengkap. Sayangnya perintah mana yang harus dijalankan atau argumen mana (comm -12, -23, -13) dalam setiap kasus tidak selalu intuitif sebagai "persimpangan" atau "perbedaan". Mungkin akan membuat pembungkus di sekitar mereka, karena saya selalu menggunakan hal-hal ini.
nilton
Saya menjalankan [pol @ localhost inst] $ grep -xc dan INSTALL-BINARY 0 [pol @ localhost inst] $ tetapi saya tidak mengerti apa artinya. Kata "dan" harus muncul berkali-kali dalam file. Apa yang saya lakukan salah?
Vérace
1
Setel persimpangan: sort set1 set2 | uniq -dtidak berfungsi untuk multi-set. Pertimbangkan untuk menggunakan sort <(sort -u set1) <(sort -u set2) | uniq -d.
neo
11

Semacam. Anda perlu berurusan dengan penyortiran diri sendiri, tetapi commdapat digunakan untuk melakukan itu, memperlakukan setiap baris sebagai anggota yang ditetapkan: -12untuk persimpangan, -13untuk perbedaan. (Dan -23memberi Anda membalik perbedaan, yaitu, set2 - set1bukannya set1 - set2.) Serikat sort -udalam pengaturan ini.

geekosaurus
sumber
1
Memang, comm tampaknya melakukan sebagian besar hal. Meskipun argumennya sangat tidak intuitif. Terima kasih!
nilton
7

Saya tidak tahu alat tertentu tetapi Anda dapat menggunakan Python, dan kelas yang ditetapkan dan operator, untuk menulis skrip kecil untuk melakukan itu.

Sebagai contoh:

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

set(['awk',
     'basename',
     'chroot', ...
Keith
sumber
Ya, jawaban yang bagus. Mengapa menggunakan awk jika python tersedia?
guettli
Anda lupa:Python> import os
James Bowery
7

Alat kecil "setop" sekarang tersedia di Debian Stretch dan di Ubuntu sejak 16.10. Anda bisa mendapatkannya via sudo apt install setop

Berikut ini beberapa contohnya. Set untuk dioperasikan diberikan sebagai file input yang berbeda: 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

Kueri Boolean hanya mengembalikan EXIT_SUCCESSjika benar, dan EXIT_FAILUREjuga pesan sebaliknya. Dengan cara ini, setop dapat digunakan dalam 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)?

Dimungkinkan juga untuk mendeskripsikan sebelumnya bagaimana aliran input harus diuraikan, sebenarnya dengan ekspresi reguler:

  • setop input.txt --input-separator "[[:space:]-]"berarti spasi putih (yaitu \v \t \n \r \fspasi) atau tanda minus ditafsirkan sebagai pemisah antara elemen (standarnya adalah baris baru, yaitu setiap baris file input adalah satu elemen)
  • setop input.txt --input-element "[A-Za-z]+" berarti elemen hanya kata-kata yang terdiri dari karakter latin, semua karakter lain dianggap sebagai pemisah antar elemen

Selanjutnya, Anda bisa

  • --count semua elemen dari set output,
  • --trim semua elemen input (yaitu, menghapus semua karakter yang sebelumnya dan sebelumnya yang tidak diinginkan seperti spasi, koma dll.),
  • pertimbangkan elemen kosong sebagai valid melalui --include-empty,
  • --ignore-case,
  • mengatur --output-separatorelemen-elemen dari aliran output (standarnya adalah \n),
  • dan seterusnya.

Lihat man setopatau github.com/phisigma/setop untuk informasi lebih lanjut.

jujur
sumber
3

Jika Anda melihat file sebagai kumpulan garis, dan file diurutkan, ada comm.

Jika Anda melihat file sebagai kumpulan (multi) baris, dan baris tidak diurutkan, grepdapat melakukan perbedaan dan persimpangan (mencapai perbedaan set dan persimpangan, tetapi tidak menghormati hitungan multiset). Persatuan itu adil cat.

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

Saya telah membuat utilitas Python yang dapat melakukan penyatuan garis, persimpangan, perbedaan, dan produk dari banyak file. Ini disebut SetOp, Anda dapat menemukannya di PyPI (di sini ). Sintaksnya terlihat seperti ini:

$ setop -i file1 file2 file3  # intersection
$ setop -d file1 file2 file3  # difference
Tigr
sumber
1

Saya menulis alat kecil untuk melakukan ini yang telah sangat berguna bagi saya di berbagai tempat. UI tidak dipoles dan saya tidak yakin tentang karakteristik kinerja untuk file yang sangat besar (karena membaca seluruh daftar ke dalam memori) tetapi "itu bekerja untuk saya". Program ini ada di https://github.com/nibrahim/lines . Ada dalam Python. Anda bisa menggunakannya pip install lines.

Saat ini mendukung penyatuan, persimpangan, perbedaan dan perbedaan simetris dari dua file. Setiap baris dari file input diperlakukan sebagai elemen dari set.

Ini juga memiliki dua operasi tambahan. Salah satu dari memeras baris kosong dalam file dan yang kedua (yang telah sangat berguna bagi saya) adalah untuk melihat file dan membaginya menjadi set string yang serupa. Saya memerlukan ini untuk mencari file dalam daftar yang tidak cocok dengan pola umum.

Saya akan menyambut umpan balik.

Noufal Ibrahim
sumber
0

Filesystem memperlakukan nama file (seluruh nama file, termasuk jalur) sebagai unik.

Operasi?

Anda dapat menyalin file di a / dan b / ke direktori kosong c /, untuk mendapatkan set union yang baru.

Dengan tes file seperti -e namedan loop atau temukan, Anda dapat memeriksa file yang ada di dua atau lebih direktori, untuk mendapatkan persimpangan, atau perbedaannya.

Pengguna tidak diketahui
sumber
1
Maksud saya memperlakukan konten file sebagai elemen set (misalkan, satu elemen per baris), dan file itu sendiri sebagai set.
nilton
0

Jawaban terbaik di sini: Setdown (alat khusus)

Saya menulis sebuah program bernama setdown yang melakukan operasi Set dari cli.

Itu dapat melakukan operasi yang ditetapkan dengan menulis definisi yang mirip dengan apa yang akan Anda tulis di Makefile:

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

Cukup keren dan Anda harus memeriksanya. Saya pribadi tidak merekomendasikan menggunakan perintah ad-hoc yang tidak dibangun untuk pekerjaan untuk melakukan operasi yang ditetapkan. Ini tidak akan bekerja dengan baik ketika Anda benar-benar perlu melakukan banyak operasi yang ditetapkan atau jika Anda memiliki operasi yang ditetapkan yang saling bergantung satu sama lain . Bukan hanya itu tetapi penurunan memungkinkan Anda menulis operasi yang tergantung pada operasi yang ditetapkan lainnya!

Bagaimanapun, saya pikir itu sangat keren dan Anda harus benar-benar memeriksanya.

Robert Massaioli
sumber
0

Pola sampel untuk banyak file (persimpangan dalam kasus ini):

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

Perluas ke:

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

File uji:

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

Keluaran:

0
6
12
18
bsb
sumber
0

Dengan zsharray ( zsharray dapat berisi urutan byte, bahkan 0).

(perhatikan juga yang dapat Anda lakukan typeset -U arrayuntuk memastikan elemen-elemennya unik).

mengatur keanggotaan

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

(menggunakan Iflag subscript array, untuk mendapatkan indeks kejadian terakhir $elementdalam array (atau 0 jika tidak ditemukan). Hapus e(untuk exact) untuk $elementdiambil sebagai pola)

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

${array:#pattern}menjadi variasi pada ksh ${var#pattern}yang menghilangkan elemen yang cocok dengan pola dan bukan hanya menghapus bagian utama yang cocok dengan pola. The (M)(untuk cocok ) membalikkan makna dan menghapus semua namun unsur cocok (gunakan $~elementuntuk itu harus diambil sebagai pola).

mengatur persimpangan

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

${set1:*set2}melakukan persimpangan array, tetapi "${(@)...}"sintaks diperlukan untuk mempertahankan elemen kosong.

mengatur kesetaraan

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

Menguji apakah array identik (dan dalam urutan yang sama). The qbendera ekspansi parameter mengutip elemen (untuk menghindari masalah dengan hal-hal seperti a=(1 "2 3")vs b=("1 2" 3)), dan (j: :)bergabung dengan mereka dengan ruang sebelum melakukan perbandingan string.

Untuk memeriksa bahwa mereka memiliki elemen yang sama, terlepas dari pesanan, gunakan obendera untuk memesannya. Lihat juga ubendera (unik) untuk menghapus duplikat.

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

mengatur kardinalitas

n=$#array

tes subset

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

Persatuan

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

(lihat di typeset -Uatas atau ubendera ekspansi parameter untuk mengambil kasus duplikat). Sekali lagi jika string kosong bukan salah satu dari nilai yang mungkin, Anda dapat menyederhanakan untuk:

union=($array1 $array2)

melengkapi

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

untuk elemen $array1yang tidak ada dalam $array2.

minimum / maksimum (perbandingan leksikal)

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

minimum / maksimum (perbandingan bilangan desimal)

min=${${(no)array}[1]} max=${${(no)array}[-1]}
Stéphane Chazelas
sumber