Odpowiedzi:
Zakładając, że elementy to ciągi znaków inne niż NUL i nowa linia (uważaj jednak, że nowa nazwa jest poprawna w nazwach plików), możesz reprezentować zestaw jako plik tekstowy z jednym elementem w linii i użyć niektórych standardowych narzędzi uniksowych.
$ 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
Wszystkie możliwe podzbiory zestawu wyświetlanej przestrzeni oddzielone, po jednym w wierszu:
$ p() { [ "$#" -eq 0 ] && echo || (shift; p "$@") |
while read r; do printf '%s %s\n%s\n' "$1" "$r" "$r"; done; }
$ p $(cat set)
(zakłada, że elementy nie zawierają SPC, TAB (przy założeniu wartości domyślnej $IFS
), odwrotnego ukośnika, symboli wieloznacznych).
$ 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
Wszystkie dostępne na stronie http://www.catonmat.net/blog/set-operations-in-unix-shell-simplified/
sort set1 set2 | uniq -d
nie działa w przypadku wielu zestawów. Rozważ użycie sort <(sort -u set1) <(sort -u set2) | uniq -d
.
Raczej. Musisz poradzić sobie z sortowaniem, ale comm
możesz tego użyć, traktując każdą linię jako zestaw elementów: -12
na skrzyżowaniu, -13
dla różnicy. (I -23
daje ci odwróconą różnicę, to znaczy set2 - set1
zamiast set1 - set2
.) Union jest sort -u
w tej konfiguracji.
Nie znam konkretnego narzędzia, ale możesz użyć Pythona, jego zestawu klas i operatorów, aby napisać mały skrypt, aby to zrobić.
Na przykład:
Python> s1 = set(os.listdir("/bin"))
Python> s2 = set(os.listdir("/usr/bin"))
Python> s1 & s2
set(['awk',
'basename',
'chroot', ...
Python> import os
Małe narzędzie konsolowe „setop” jest teraz dostępne w Debian Stretch i Ubuntu od 16.10. Możesz to uzyskać za pośrednictwem
sudo apt install setop
Oto kilka przykładów. Zestawy do obsługi są podane jako różne pliki wejściowe:
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
Zapytania boolowskie zwracane są tylko EXIT_SUCCESS
w przypadku wartości true, a EXIT_FAILURE
także w innym przypadku. W ten sposób setop może być używany w powłoce.
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)?
Możliwe jest również precyzyjne opisanie sposobu analizowania strumieni wejściowych, w rzeczywistości za pomocą wyrażeń regularnych:
setop input.txt --input-separator "[[:space:]-]"
oznacza, że spacja (tj. \v
\t
\n
\r
\f
lub spacja) lub znak minus jest interpretowany jako separator między elementami (domyślnie jest to nowa linia, tzn. każda linia pliku wejściowego to jeden element)setop input.txt --input-element "[A-Za-z]+"
oznacza, że elementy to tylko słowa składające się ze znaków łacińskich, wszystkie pozostałe znaki są uważane za separatory między elementamiPonadto możesz
--count
wszystkie elementy zestawu wyjściowego,--trim
wszystkie elementy wejściowe (tj. usuwają wszystkie niechciane poprzedzające i następujące po sobie znaki, takie jak spacja, przecinek itp.),--include-empty
,--ignore-case
,--output-separator
między elementami strumienia wyjściowego (domyślnie jest \n
),Aby uzyskać więcej informacji, zobacz man setop
lub github.com/phisigma/setop .
Jeśli widzisz plik jako zestaw linii, a pliki są posortowane, oznacza to, że istnieje comm
.
Jeśli widzisz plik jako (wielokrotny) zestaw linii, a linie nie są posortowane, grep
może robić różnicę i przecięcie (osiąga ustawioną różnicę i przecięcie, ale nie uwzględnia liczenia dla wielu zestawów). Unia jest sprawiedliwa cat
.
grep -xF -f small large >intersection
grep -vxF -f small large >difference
cat small large >union
Napisałem małe narzędzie do tego, które było mi bardzo przydatne w różnych miejscach. Interfejs użytkownika nie jest dopracowany i nie jestem pewien co do wydajności bardzo dużych plików (ponieważ wczytuje całą listę do pamięci), ale „działa dla mnie”. Program znajduje się na https://github.com/nibrahim/lines . Jest w Pythonie. Możesz to uzyskać za pomocą pip install lines
.
Obecnie obsługuje łączenie, przecięcie, różnicę i różnicę symetryczną dwóch plików. Każdy wiersz pliku wejściowego jest traktowany jako element zestawu.
Ma również dwie dodatkowe operacje. Jednym z wyciskania pustych linii w pliku, a drugim (co było dla mnie bardzo przydatne) jest przeglądanie pliku i dzielenie go na zestawy podobnych ciągów. Potrzebowałem tego, aby wyszukać pliki na liście, które nie pasują do ogólnego wzorca.
Chciałbym poznać opinie.
System plików traktuje nazwy plików (całe nazwy plików, w tym ścieżki) jako unikalne.
Operacje
Możesz skopiować pliki z / ib / do pustego katalogu c /, aby uzyskać nowy zestaw unii.
Dzięki testom plików, takim jak -e name
i pętle lub find, możesz sprawdzić pliki istniejące w dwóch lub więcej katalogach, aby uzyskać skrzyżowanie lub różnicę.
Najlepsza odpowiedź tutaj: Setdown (dedykowane narzędzie)
Napisałem program o nazwie setdown, który wykonuje operacje Set z cli.
Może wykonywać operacje ustawiania, pisząc definicję podobną do tego, co napisałeś w Makefile:
someUnion: "file-1.txt" \/ "file-2.txt"
someIntersection: "file-1.txt" /\ "file-2.txt"
someDifference: someUnion - someIntersection
Jest całkiem fajny i powinieneś to sprawdzić. Ja osobiście nie polecam używania komend ad-hoc, które nie zostały zbudowane dla zadania do wykonywania ustawionych operacji. Nie zadziała to dobrze, gdy naprawdę musisz wykonać wiele operacji ustawiania lub jeśli masz jakieś operacje ustawiania, które są od siebie zależne . Poza tym setdown pozwala pisać operacje na zestawach, które zależą od innych operacji na zestawach!
W każdym razie uważam, że to całkiem fajne i powinieneś to całkowicie sprawdzić.
Z zsh
tablicami ( zsh
tablice mogą zawierać dowolną sekwencję bajtów, nawet 0).
(pamiętaj też, że możesz zrobić, typeset -U array
aby zagwarantować, że jego elementy są unikalne).
if ((${array[(Ie)$element]})); then
echo '$element is in $array'
fi
(za pomocą I
flagi indeksu tablicy, aby uzyskać indeks ostatniego wystąpienia $element
w tablicy (lub 0, jeśli nie znaleziono). Usuń e
(dla e
xact), $element
aby zostać wziętym jako wzorzec)
if ((n = ${(M)#array:#$element})); then
echo "\$element is found $n times in \$array'
fi
${array:#pattern}
jest odmianą ksh, ${var#pattern}
która usuwa elementy pasujące do wzorca, a nie tylko usuwa wiodącą część pasującą do wzorca. Opcja (M)
(dla dopasowanego ) odwraca znaczenie i usuwa wszystkie elementy oprócz dopasowanych (użyj $~element
, aby wziąć to jako wzorzec).
common=("${(@)set1:*set2}")
${set1:*set2}
robi przecięcie tablicy, ale "${(@)...}"
składnia jest potrzebna do zachowania pustych elementów.
[[ ${(j: :)${(q)array1}} = ${(j: :)${(q)array2}} ]]
Sprawdza, czy tablice są identyczne (i w tej samej kolejności). q
Flag ekspansja parametr cytuje elementów (w celu uniknięcia problemów z rzeczy, takich jak a=(1 "2 3")
vs b=("1 2" 3)
) i (j: :)
łączy je z przestrzeni przed wykonaniem porównania ciągów.
Aby sprawdzić, czy mają te same elementy, niezależnie od kolejności, użyj o
flagi, aby je zamówić. Zobacz także u
flagę (unikalną), aby usunąć duplikaty.
[[ ${(j: :)${(qo)array1}} = ${(j: :)${(qo)array2}} ]]
n=$#array
if ((${#array1:*array2} == ${#array2})); then
echo '$array2 is included in $array1'
fi
union=("$array1[@]" "$array2[@]")
(patrz typeset -U
wyżej lub u
flaga rozwinięcia parametru, aby uwzględnić przypadki duplikatów). Ponownie, jeśli pusty ciąg nie jest jedną z możliwych wartości, możesz uprościć:
union=($array1 $array2)
complement=("${(@)array1:|array2}")
dla elementów $array1
, których nie ma w $array2
.
min=${${(o)array}[1]} max=${${(o)array}[-1]}
min=${${(no)array}[1]} max=${${(no)array}[-1]}