Risposte:
Supponendo che gli elementi siano stringhe di caratteri diversi da NUL e newline (attenzione, tuttavia, che newline sia valida nei nomi dei file), è possibile rappresentare un set come file di testo con un elemento per riga e utilizzare alcune delle utility Unix standard.
$ 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
Tutti i possibili sottoinsiemi di uno spazio visualizzato impostato sono separati, uno per riga:
$ p() { [ "$#" -eq 0 ] && echo || (shift; p "$@") |
while read r; do printf '%s %s\n%s\n' "$1" "$r" "$r"; done; }
$ p $(cat set)
(presuppone che gli elementi non contengano SPC, TAB (assumendo il valore predefinito di $IFS
), barra rovesciata, caratteri jolly).
$ 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
Tutti disponibili su http://www.catonmat.net/blog/set-operations-in-unix-shell-simplified/
sort set1 set2 | uniq -d
non funziona per più insiemi. Prendi in considerazione l'utilizzo sort <(sort -u set1) <(sort -u set2) | uniq -d
.
Una specie di. Devi occuparti dell'ordinamento da solo, ma comm
puoi usarlo, trattando ogni linea come un membro impostato: -12
per l'intersezione, -13
per la differenza. (E -23
ti dà la differenza capovolta, cioè al set2 - set1
posto di set1 - set2
.) Union è sort -u
in questa configurazione.
Non conosco uno strumento specifico ma puoi usare Python, e la sua classe set e operatori, per scrivere un piccolo script per farlo.
Per esempio:
Python> s1 = set(os.listdir("/bin"))
Python> s2 = set(os.listdir("/usr/bin"))
Python> s1 & s2
set(['awk',
'basename',
'chroot', ...
Python> import os
Il piccolo strumento per console "setop" è ora disponibile in Debian Stretch e in Ubuntu dal 16.10. Puoi ottenerlo tramite
sudo apt install setop
Ecco alcuni esempi. I set su cui operare sono dati come file di input diversi:
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
Le query booleane vengono restituite solo EXIT_SUCCESS
in caso di true e in caso EXIT_FAILURE
contrario un messaggio. In questo modo, setop può essere utilizzato nella 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)?
È anche possibile descrivere in modo preciso come devono essere analizzati i flussi di input, in realtà mediante espressioni regolari:
setop input.txt --input-separator "[[:space:]-]"
significa che uno spazio bianco (ovvero \v
\t
\n
\r
\f
uno spazio) o un segno meno viene interpretato come un separatore tra gli elementi (il valore predefinito è una nuova riga, ovvero ogni riga del file di input è un elemento)setop input.txt --input-element "[A-Za-z]+"
significa che gli elementi sono solo parole costituite da caratteri latini, tutti gli altri caratteri sono considerati separatori tra gli elementiInoltre, puoi farlo
--count
tutti gli elementi del set di output,--trim
tutti gli elementi di input (cioè cancellare tutti i caratteri precedenti e successivi indesiderati come spazio, virgola ecc.),--include-empty
,--ignore-case
,--output-separator
tra gli elementi del flusso di output (l'impostazione predefinita è \n
),Vedere man setop
o github.com/phisigma/setop per ulteriori informazioni.
Se vedi un file come un insieme di linee e i file sono ordinati, c'è comm
.
Se vedi un file come un (multi) insieme di linee e le linee non sono ordinate, grep
può fare la differenza e l'intersezione (ottiene la differenza e l'intersezione impostate, ma non rispetta il conteggio per i multiset). L'unione è giusta cat
.
grep -xF -f small large >intersection
grep -vxF -f small large >difference
cat small large >union
Ho creato un'utilità Python in grado di eseguire l'unione, l'intersezione, la differenza e il prodotto di più file. Si chiama SetOp, lo puoi trovare su PyPI ( qui ). La sintassi è simile alla seguente:
$ setop -i file1 file2 file3 # intersection
$ setop -d file1 file2 file3 # difference
Ho scritto un piccolo strumento per farlo, che mi è stato molto utile in vari luoghi. L'interfaccia utente non è lucidata e non sono sicuro delle caratteristiche prestazionali per file molto grandi (poiché legge l'intero elenco in memoria) ma "funziona per me". Il programma è su https://github.com/nibrahim/lines . È in Python. Puoi ottenerlo usando pip install lines
.
Attualmente supporta unione, intersezione, differenza e differenza simmetrica di due file. Ogni riga del file di input viene trattata come un elemento di un set.
Ha anche due operazioni extra. Uno di spremere le righe vuote in un file e il secondo (che è stato molto utile per me) è quello di guardare attraverso il file e dividerlo in serie di stringhe simili. Ne avevo bisogno per cercare i file in un elenco che non corrispondevano al modello generale.
Gradirei feedback.
Il filesystem tratta i nomi di file (nomi di file interi, inclusi i percorsi) come unici.
Le operazioni?
È possibile copiare i file in a / eb / nella directory vuota c /, per ottenere un nuovo set di unione.
Con test di file come -e name
e loop o find, è possibile verificare la presenza di file esistenti in due o più directory, per ottenere l'intersezione o la differenza.
La migliore risposta qui: Setdown (uno strumento dedicato)
Ho scritto un programma chiamato setdown che esegue le operazioni Set dal cli.
Può eseguire operazioni di set scrivendo una definizione simile a quella che si scriverebbe in un Makefile:
someUnion: "file-1.txt" \/ "file-2.txt"
someIntersection: "file-1.txt" /\ "file-2.txt"
someDifference: someUnion - someIntersection
È piuttosto bello e dovresti dare un'occhiata. Personalmente non raccomando di usare comandi ad hoc che non sono stati creati per il lavoro per eseguire operazioni di set. Non funzionerà bene quando è davvero necessario eseguire molte operazioni di set o se ci sono operazioni di set che dipendono l'una dall'altra . Non solo, ma setdown ti consente di scrivere operazioni set che dipendono da altre operazioni set!
Ad ogni modo, penso che sia abbastanza bello e dovresti assolutamente provarlo.
Con le zsh
matrici (le zsh
matrici possono contenere qualsiasi sequenza arbitraria di byte, anche 0).
(nota anche che puoi fare typeset -U array
per garantire che i suoi elementi siano unici).
if ((${array[(Ie)$element]})); then
echo '$element is in $array'
fi
(utilizzando il I
flag di indice dell'array, per ottenere l'indice dell'ultima occorrenza $element
dell'array (o 0 se non trovato). Rimuovi e
(per e
xact) per $element
essere preso come modello)
if ((n = ${(M)#array:#$element})); then
echo "\$element is found $n times in \$array'
fi
${array:#pattern}
essendo una variazione su ksh ${var#pattern}
che rimuove gli elementi che corrispondono al modello invece di rimuovere semplicemente la parte iniziale che corrisponde al modello. Il (M)
(per abbinato ) inverte il significato e rimuove tutti gli elementi tranne quelli abbinati (usalo $~element
per essere preso come modello).
common=("${(@)set1:*set2}")
${set1:*set2}
esegue l'intersezione di array, ma la "${(@)...}"
sintassi è necessaria per conservare gli elementi vuoti.
[[ ${(j: :)${(q)array1}} = ${(j: :)${(q)array2}} ]]
Verifica se le matrici sono identiche (e nello stesso ordine). Il q
flag di espansione dei parametri cita gli elementi (per evitare problemi con cose come a=(1 "2 3")
vs b=("1 2" 3)
) e (j: :)
li unisce con lo spazio prima di fare un confronto tra stringhe.
Per verificare che abbiano gli stessi elementi, indipendentemente dall'ordine, usa la o
bandiera per ordinarli. Vedi anche il u
flag (unico) per rimuovere i duplicati.
[[ ${(j: :)${(qo)array1}} = ${(j: :)${(qo)array2}} ]]
n=$#array
if ((${#array1:*array2} == ${#array2})); then
echo '$array2 is included in $array1'
fi
union=("$array1[@]" "$array2[@]")
(vedi typeset -U
sopra o il u
flag di espansione del parametro per prendere il caso di duplicati). Ancora una volta se la stringa vuota non è uno dei possibili valori, è possibile semplificare per:
union=($array1 $array2)
complement=("${(@)array1:|array2}")
per gli elementi di $array1
quello non sono in $array2
.
min=${${(o)array}[1]} max=${${(o)array}[-1]}
min=${${(no)array}[1]} max=${${(no)array}[-1]}