Jawaban:
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.
$ 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
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).
$ 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
Semua tersedia di http://www.catonmat.net/blog/set-operations-in-unix-shell-simplified/
sort set1 set2 | uniq -d
tidak berfungsi untuk multi-set. Pertimbangkan untuk menggunakan sort <(sort -u set1) <(sort -u set2) | uniq -d
.
Semacam. Anda perlu berurusan dengan penyortiran diri sendiri, tetapi comm
dapat digunakan untuk melakukan itu, memperlakukan setiap baris sebagai anggota yang ditetapkan: -12
untuk persimpangan, -13
untuk perbedaan. (Dan -23
memberi Anda membalik perbedaan, yaitu, set2 - set1
bukannya set1 - set2
.) Serikat sort -u
dalam pengaturan ini.
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', ...
Python> import os
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_SUCCESS
jika benar, dan EXIT_FAILURE
juga 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
\f
spasi) 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 elemenSelanjutnya, 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.),--include-empty
,--ignore-case
,--output-separator
elemen-elemen dari aliran output (standarnya adalah \n
),Lihat man setop
atau github.com/phisigma/setop untuk informasi lebih lanjut.
Jika Anda melihat file sebagai kumpulan garis, dan file diurutkan, ada comm
.
Jika Anda melihat file sebagai kumpulan (multi) baris, dan baris tidak diurutkan, grep
dapat 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
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
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.
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 name
dan loop atau temukan, Anda dapat memeriksa file yang ada di dua atau lebih direktori, untuk mendapatkan persimpangan, atau perbedaannya.
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.
Dengan zsh
array ( zsh
array dapat berisi urutan byte, bahkan 0).
(perhatikan juga yang dapat Anda lakukan typeset -U array
untuk memastikan elemen-elemennya unik).
if ((${array[(Ie)$element]})); then
echo '$element is in $array'
fi
(menggunakan I
flag subscript array, untuk mendapatkan indeks kejadian terakhir $element
dalam array (atau 0 jika tidak ditemukan). Hapus e
(untuk e
xact) untuk $element
diambil 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 $~element
untuk itu harus diambil sebagai pola).
common=("${(@)set1:*set2}")
${set1:*set2}
melakukan persimpangan array, tetapi "${(@)...}"
sintaks diperlukan untuk mempertahankan elemen kosong.
[[ ${(j: :)${(q)array1}} = ${(j: :)${(q)array2}} ]]
Menguji apakah array identik (dan dalam urutan yang sama). The q
bendera 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 o
bendera untuk memesannya. Lihat juga u
bendera (unik) untuk menghapus duplikat.
[[ ${(j: :)${(qo)array1}} = ${(j: :)${(qo)array2}} ]]
n=$#array
if ((${#array1:*array2} == ${#array2})); then
echo '$array2 is included in $array1'
fi
union=("$array1[@]" "$array2[@]")
(lihat di typeset -U
atas atau u
bendera 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)
complement=("${(@)array1:|array2}")
untuk elemen $array1
yang tidak ada dalam $array2
.
min=${${(o)array}[1]} max=${${(o)array}[-1]}
min=${${(no)array}[1]} max=${${(no)array}[-1]}