Dosyaları kümeler olarak ele almak ve üzerlerinde küme işlemleri gerçekleştirmek için Linux araçları


81

Özel olarak dosyaları kümeler olarak ele almak ve üzerlerinde set işlemleri gerçekleştirmek için özel olarak tasarlanmış herhangi bir linux aracı tanıyor mu? Fark, kesişme vb.

Yanıtlar:


110

Öğelerin NUL ve newline dışındaki karakter dizeleri olduğunu varsayalım (newline'ın dosya adlarında geçerli olduğuna dikkat edin), bir kümeyi her satırda bir öğe içeren bir metin dosyası olarak temsil edebilir ve standart Unix yardımcı programlarından bazılarını kullanabilirsiniz.

Üyeliği Ayarla

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

Kavşağı Ayarla

$ 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

Eşitliği Ayarla

$ 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

Kardinalliği Ayarla

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

$ awk 'END { print NR }' set

$ sed '$=' set

Altküme Testi

$ 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

Birliği ayarla

$ 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

Tamamlayıcıyı Ayarla

$ 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

Simetrik Farkı Ayarla

$ 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

Gücü ayarla

Bir kümenin tüm olası alt kümeleri, satır başına bir tane olmak üzere ayrılmış bir alan görüntülüyor:

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

(öğelerin SPC, SEKME (varsayılan değer olduğu varsayılarak $IFS), ters eğik çizgi, joker karakterler içermediğini varsayar ).

Kartezyen Ürünü Ayarla

$ 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

Ayrık Küme Testi

$ 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

Boş Set Testi

$ 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

Hepsi http://www.catonmat.net/blog/set-operations-in-unix-shell-simplified/ adresinde bulunabilir.


1
Python versiyonunun çok daha basit ve sezgisel olduğunu düşünüyorum. ;-)
Keith,

Bunun en eksiksiz cevap olduğunu düşünüyorum. Ne yazık ki, hangi komutların çalıştırılacağı veya hangi durumlarda hangi komutların (comm -12, -23, -13) "kavşak" veya "fark" olduğu her zaman sezgisel değildir. Belki etraflarında bir sarmalayıcı yaratacağım, çünkü hep bunları kullanıyorum.
nilton

[Pol @ localhost inst] $ grep -xc ve INSTALL-BINARY 0 [pol @ localhost inst] $ kullandım, ancak ne anlama geldiğini anlamadım. Dosyada "ve" kelimesi birçok kez bulunmalıdır. Neyi yanlış yapıyorum?
Vérace

1
Kesişme ayarı: sort set1 set2 | uniq -dçoklu kümeler için çalışmaz. Kullanmayı düşünün sort <(sort -u set1) <(sort -u set2) | uniq -d.
neo

11

Sırala. Kendinizi sıralamakla başa çıkmanız gerekir, ancak commbunu yapmak için kullanılabilir, her bir çizgiyi belirlenmiş bir üye olarak ele alır: -12kavşak -13için, fark için. (Ve -23yani,, farkı çevrilmiş verir set2 - set1yerine set1 - set2.) Birliği olan sort -ubu kurulum.


1
Nitekim, iletişim çoğu şeyi yapmak gibi görünüyor. Argümanlar çok sezgisel olmasa da. Teşekkürler!
nilton

7

Belirli bir aracı bilmiyorum ama bunu yapmak için küçük bir betik yazmak için Python'u ve set sınıfını ve işleçlerini kullanabilirsiniz.

Sınav için:

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

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

Evet, güzel cevap. Python varsa neden awk kullanılır?
guettli

Unuttun:Python> import os
James Bowery

7

Küçük konsol aracı “setop”, 16.10'dan beri Debian Stretch ve Ubuntu'da mevcut. Üzerinden alabilirsiniz sudo apt install setop

İşte bazı örnekler. Çalıştırılacak setler farklı giriş dosyaları olarak verilmiştir: 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

Boolean sorguları yalnızca EXIT_SUCCESSdoğru olduğunda ve EXIT_FAILUREaksi takdirde bir mesaj olduğunda geri döner . Bu şekilde, setop kabukta kullanılabilir. 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)?

Girdi akışlarının nasıl düzenli olarak ifade edileceği ile nasıl ayrıştırılacağını tam olarak açıklamak da mümkündür:

  • setop input.txt --input-separator "[[:space:]-]"bir boşluk (yani \v \t \n \r \fveya boşluk) veya bir eksi işaretinin öğeler arasında bir ayırıcı olarak yorumlandığı anlamına gelir (varsayılan değer yeni satırdır, yani giriş dosyasının her satırı bir öğedir).
  • setop input.txt --input-element "[A-Za-z]+" elemanların yalnızca latin karakterlerinden oluşan sözcükler olduğu anlamına gelir, diğer tüm karakterlerin öğeler arasında ayırıcı olduğu kabul edilir.

Ayrıca, yapabilirsiniz

  • --count çıkış kümesinin tüm elemanları,
  • --trim tüm girdi öğeleri (yani boşluk, virgül vb. önceki tüm istenmeyen ve sonraki karakterleri siler),
  • boş elemanları, üzerinden geçerli olarak kabul etmek --include-empty,
  • --ignore-case,
  • --output-separatorÇıkış akışının elemanları arasındakileri ayarlayın (varsayılan \n)
  • ve bunun gibi.

Bkz man setopveya github.com/phisigma/setop fazla bilgi için.


3

Bir dosyayı satır kümesi olarak görüyorsanız ve dosyalar sıralanırsa, var comm.

Bir dosyayı (çok) satır kümesi olarak görüyorsanız ve satırlar sıralanmamışsa, grepfark ve kesişme yapabilir (ayarlanan fark ve kesişme elde eder, ancak çoklu kümelerin sayımına saygı göstermez). Sendika adil cat.

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

2

Birden fazla dosyanın çizgi birleşimini, kesişimini, farkını ve ürününü yapabilen bir Python yardımcı programı yaptım. Buna SetOp, PyPI'de ( burada ) bulabilirsiniz. Sözdizimi şuna benzer:

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

1

Bunu yapmak için çeşitli yerlerde benim için oldukça faydalı olan küçük bir araç yazdım. UI cilalanmamış ve çok büyük dosyaların performans özelliklerini (tüm listeyi belleğe okuduğundan) emin değilim ama "benim için çalışıyor". Program https://github.com/nibrahim/lines adresinde yer almaktadır . Python'da. Kullanarak alabilirsiniz pip install lines.

Şu anda iki dosyanın birliğini, kesişimini, farkını ve simetrik farkını desteklemektedir. Giriş dosyasının her satırı bir kümenin öğesi olarak değerlendirilir.

Aynı zamanda iki ekstra işlem daha var. Bir dosyadaki boş satırları sıkmak ve ikincisi (benim için çok faydalı oldu) dosyaya bakmak ve onu benzer dizgilerin kümelerine bölmektir. Genel modelle uyuşmayan bir listedeki dosyaları aramak için buna ihtiyacım vardı.

Geribildirimden memnuniyet duyarım.


0

Dosya sistemi dosya adlarını (yollar dahil tüm dosya adları) benzersiz olarak değerlendirir.

Operasyonlar?

Yeni bir birleşim kümesi elde etmek için a / ve b / 'deki dosyaları boş c / c dizinine kopyalayabilirsiniz.

-e nameKesişme veya farkı bulmak için, dosyalarda ya da döngülerde bulunan dosya testleriyle , iki veya daha fazla dizinde bulunan dosyaları kontrol edebilirsiniz.


1
Dosyaların içeriğini bir kümenin elemanları (örneğin her satır için bir öğe) ve dosyaları da kümeler olarak ele almak istemiştim .
nilton

0

Buradaki en iyi cevap: Setdown (özel bir araç)

Set işlemlerini cli'dan gerçekleştiren setdown adlı bir program yazdım.

Makefile'de yazdıklarınıza benzer bir tanım yazarak set işlemlerini gerçekleştirebilir:

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

Oldukça havalı ve kontrol etmelisin. Şahsen ben set işlemleri yapmak için iş için oluşturulmamış geçici komutları kullanmanızı önermiyorum. Gerçekten birçok set işlemi yapmanız gerektiğinde veya birbirine bağlı herhangi bir işleminiz varsa, iyi çalışmaz. . Sadece bu değil, setdown, diğer set işlemlerine bağlı set işlemleri yazmanıza izin verir!

Her halükarda, bence oldukça havalı ve tamamen kontrol etmelisin.


0

Birden fazla dosya için örnek kalıp (bu durumda kesişme):

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

Şunlara genişletilir:

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

Test dosyaları:

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

Çıktı:

0
6
12
18

0

İle zshdiziler ( zshdiziler bayt herhangi bir rasgele sekansı, hatta 0 içerebilir).

(ayrıca typeset -U array, öğelerinin benzersiz olduğunu garanti etmek için yapabileceğinizi unutmayın ).

üyelik ayarla

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

(kullanarak Ison görüldüğü dizinini elde etmek için, dizi simge bayrağı $elementbulunmazsa dizisinde (ya da 0). çıkarmak eiçin ( eiçin) XACT $elementbir model olarak alınmalıdır)

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

${array:#pattern}Ksh yıllardan bir varyasyon olmak kaldırır sadece kalıpla eşleşen lider kısmını kaldırmak aksine desenle eşleşen elemanları. (İçin eşleştirilmiş ) anlama tersine döner ve kaldırır fakat eşleşen elemanlar (kullanımı için bir model olarak kabul edilebilir).${var#pattern}(M)$~element

kavşak ayarlamak

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

${set1:*set2}dizi kesişimi yapar, ancak "${(@)...}"boş öğeleri korumak için sözdizimi gerekir.

eşitlik ayarlamak

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

Dizilerin aynı olup olmadığını test eder (ve aynı sırada). qParametre genişletme bayrağı (gibi şeylerle sorunlardan kaçınmak için elemanlar tırnak a=(1 "2 3")vs b=("1 2" 3)) ve (j: :)bir dize karşılaştırma yapmadan önce boşluk onlara katılır.

Aynı öğelere sahip olduklarını kontrol etmek için, sırasına bakmaksızın, sıralamak için obayrağı kullanın. Ayrıca uçoğaltmaları kaldırmak için bayrağa (benzersiz) bakın .

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

önem düzeyi ayarla

n=$#array

alt küme testi

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

Birlik

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

(yukarıya bakın typeset -Uveya uyinelenenleri almak için parametre genişletme bayrağına bakın ). Yine, boş dize olası değerlerden biri değilse, aşağıdakileri basitleştirebilirsiniz:

union=($array1 $array2)

Tamamlayıcı

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

Bunun unsurları içinde $array1değil $array2.

minimum / maksimum (sözcük karşılaştırması)

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

minimum / maksimum (ondalık tam sayı karşılaştırması)

min=${${(no)array}[1]} max=${${(no)array}[-1]}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.