Respuestas:
Suponiendo que los elementos son cadenas de caracteres distintos de NUL y nueva línea (tenga en cuenta que la nueva línea es válida en los nombres de archivo), puede representar un conjunto como un archivo de texto con un elemento por línea y utilizar algunas de las utilidades estándar de Unix.
$ 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
Todos los subconjuntos posibles de un conjunto muestran espacios separados, uno por línea:
$ p() { [ "$#" -eq 0 ] && echo || (shift; p "$@") |
while read r; do printf '%s %s\n%s\n' "$1" "$r" "$r"; done; }
$ p $(cat set)
(asume que los elementos no contienen SPC, TAB (asumiendo el valor predeterminado de $IFS
), barra invertida, caracteres comodín).
$ 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
Todo disponible en http://www.catonmat.net/blog/set-operations-in-unix-shell-simplified/
sort set1 set2 | uniq -d
no funciona para conjuntos múltiples. Considera usar sort <(sort -u set1) <(sort -u set2) | uniq -d
.
Algo así como. Debe lidiar con la clasificación usted mismo, pero comm
puede usarse para hacer eso, tratando cada línea como un miembro fijo: -12
por intersección, -13
por diferencia. (Y -23
le da una diferencia invertida, es decir, en set2 - set1
lugar de set1 - set2
). Union está sort -u
en esta configuración.
No conozco una herramienta específica, pero puede usar Python, y su clase de conjunto y operadores, para escribir un pequeño script para hacer eso.
Por ejemplo:
Python> s1 = set(os.listdir("/bin"))
Python> s2 = set(os.listdir("/usr/bin"))
Python> s1 & s2
set(['awk',
'basename',
'chroot', ...
Python> import os
La pequeña herramienta de consola "setop" ahora está disponible en Debian Stretch y en Ubuntu desde la 16.10. Puedes obtenerlo a través de
sudo apt install setop
Aquí hay unos ejemplos. Los conjuntos a operar se proporcionan como diferentes archivos de entrada:
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
Las consultas booleanas solo se devuelven EXIT_SUCCESS
en caso de ser verdaderas, y EXIT_FAILURE
también un mensaje en caso contrario De esta manera, setop se puede usar en el 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)?
También es posible describir con precisión cómo se analizarán los flujos de entrada, en realidad mediante expresiones regulares:
setop input.txt --input-separator "[[:space:]-]"
significa que un espacio en blanco (es decir, \v
\t
\n
\r
\f
o espacio) o un signo menos se interpreta como un separador entre elementos (el valor predeterminado es una nueva línea, es decir, cada línea del archivo de entrada es un elemento)setop input.txt --input-element "[A-Za-z]+"
significa que los elementos son solo palabras que consisten en caracteres latinos, todos los demás caracteres se consideran separadores entre elementosAdemás, puedes
--count
todos los elementos del conjunto de salida,--trim
todos los elementos de entrada (es decir, borrar todos los caracteres anteriores y posteriores no deseados como espacio, coma, etc.)--include-empty
,--ignore-case
,--output-separator
elementos entre la secuencia de salida (el valor predeterminado es \n
),Visite man setop
o github.com/phisigma/setop para obtener más información.
Si ve un archivo como un conjunto de líneas, y los archivos están ordenados, hay comm
.
Si ve un archivo como un conjunto (múltiple) de líneas, y las líneas no están ordenadas, grep
puede hacer la diferencia y la intersección (logra la diferencia y la intersección establecidas, pero no respeta el recuento de varios conjuntos). La unión es justa cat
.
grep -xF -f small large >intersection
grep -vxF -f small large >difference
cat small large >union
Escribí una pequeña herramienta para hacer esto que me ha sido muy útil en varios lugares. La interfaz de usuario no está pulida y no estoy seguro de las características de rendimiento para archivos muy grandes (ya que lee la lista completa en la memoria) pero "funciona para mí". El programa está en https://github.com/nibrahim/lines . Está en Python. Puedes conseguirlo usando pip install lines
.
Actualmente admite unión, intersección, diferencia y diferencia simétrica de dos archivos. Cada línea del archivo de entrada se trata como un elemento de un conjunto.
También tiene dos operaciones adicionales. Una de las líneas en blanco en un archivo y la segunda (que me ha sido muy útil) es mirar a través del archivo y dividirlo en conjuntos de cadenas similares. Necesitaba esto para buscar archivos en una lista que no coincide con el patrón general.
Agradecería cualquier comentario.
El sistema de archivos trata los nombres de archivo (nombres de archivo completos, incluidas las rutas) como únicos.
Operaciones?
Puede copiar los archivos en a / yb / en el directorio vacío c /, para obtener un nuevo conjunto de unión.
Con pruebas de archivos como -e name
y bucles o find, puede verificar los archivos existentes en dos o más directorios, para obtener la intersección o la diferencia.
La mejor respuesta aquí: Setdown (una herramienta dedicada)
Escribí un programa llamado setdown que realiza operaciones Set desde el cli.
Puede realizar operaciones de conjunto escribiendo una definición similar a la que escribiría en un Makefile:
someUnion: "file-1.txt" \/ "file-2.txt"
someIntersection: "file-1.txt" /\ "file-2.txt"
someDifference: someUnion - someIntersection
Es genial y deberías echarle un vistazo. Personalmente, no recomiendo usar comandos ad-hoc que no se hayan creado para el trabajo para realizar operaciones de conjuntos. No funcionará bien cuando realmente necesite realizar muchas operaciones de conjuntos o si tiene alguna operación de conjunto que dependa entre sí . ¡No solo eso, sino que setdown te permite escribir operaciones de set que dependen de otras operaciones de set!
En cualquier caso, creo que es genial y deberías echarle un vistazo.
Patrón de muestra para múltiples archivos (intersección en este caso):
eval `perl -le 'print "cat ",join(" | grep -xF -f- ", @ARGV)' t*`
Se expande a:
cat t1 | grep -xF -f- t2 | grep -xF -f- t3
Archivos de prueba:
seq 0 20 | tee t1; seq 0 2 20 | tee t2; seq 0 3 20 | tee t3
Salida:
0
6
12
18
Con zsh
matrices (las zsh
matrices pueden contener cualquier secuencia arbitraria de bytes, incluso 0).
(también tenga en cuenta que puede hacer typeset -U array
para garantizar que sus elementos sean únicos).
if ((${array[(Ie)$element]})); then
echo '$element is in $array'
fi
(usando el I
indicador de subíndice de matriz, para obtener el índice de la última aparición de $element
en la matriz (o 0 si no se encuentra). Elimine e
(para e
xact) para $element
que se tome como un patrón)
if ((n = ${(M)#array:#$element})); then
echo "\$element is found $n times in \$array'
fi
${array:#pattern}
siendo una variación de ksh ${var#pattern}
que elimina los elementos que coinciden con el patrón en lugar de simplemente eliminar la parte principal que coincide con el patrón. El (M)
(para coincidencia ) invierte el significado y elimina todos los elementos excepto los coincidentes (utilícelo $~element
para tomarlo como un patrón).
common=("${(@)set1:*set2}")
${set1:*set2}
hace la intersección de la matriz, pero la "${(@)...}"
sintaxis es necesaria para preservar elementos vacíos
[[ ${(j: :)${(q)array1}} = ${(j: :)${(q)array2}} ]]
Comprueba si las matrices son idénticas (y en el mismo orden). El q
indicador de expansión de parámetros cita los elementos (para evitar problemas con cosas como a=(1 "2 3")
vs b=("1 2" 3)
), y los (j: :)
une con espacio antes de hacer una comparación de cadenas.
Para verificar que tengan los mismos elementos, independientemente del orden, use la o
bandera para ordenarlos. Vea también la u
bandera (única) para eliminar duplicados.
[[ ${(j: :)${(qo)array1}} = ${(j: :)${(qo)array2}} ]]
n=$#array
if ((${#array1:*array2} == ${#array2})); then
echo '$array2 is included in $array1'
fi
union=("$array1[@]" "$array2[@]")
(ver typeset -U
arriba o el u
indicador de expansión de parámetros para tomar el caso de duplicados). Nuevamente, si la cadena vacía no es uno de los valores posibles, puede simplificar a:
union=($array1 $array2)
complement=("${(@)array1:|array2}")
para los elementos de $array1
que no están en $array2
.
min=${${(o)array}[1]} max=${${(o)array}[-1]}
min=${${(no)array}[1]} max=${${(no)array}[-1]}