Herramientas de Linux para tratar archivos como conjuntos y realizar operaciones de conjunto en ellos


82

¿Alguien sabe de alguna herramienta de Linux específicamente diseñada para tratar archivos como conjuntos y realizar operaciones de conjunto en ellos? ¿Como diferencia, intersección, etc.?

Respuestas:


110

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.

Establecer membresía

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

Establecer intersección

$ 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

Establecer igualdad

$ 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

Establecer cardinalidad

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

$ awk 'END { print NR }' set

$ sed '$=' set

Prueba de subconjunto

$ 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

Establecer unión

$ 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

Establecer complemento

$ 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

Establecer diferencia simétrica

$ 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

Set de poder

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).

Establecer producto cartesiano

$ 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

Prueba de conjunto disjunto

$ 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

Prueba de conjunto vacío

$ 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

Mínimo

$ 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

Máximo

$ 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/


1
Creo que la versión de Python es mucho más simple e intuitiva. ;-)
Keith el

Creo que esta es la respuesta más completa. Desafortunadamente, qué comandos ejecutar o qué argumentos (comm -12, -23, -13) en cada caso no siempre son intuitivos como "intersección" o "diferencia". Tal vez cree un envoltorio alrededor de ellos, ya que siempre estoy usando estas cosas.
nilton

Ejecuté [pol @ localhost inst] $ grep -xc e INSTALL-BINARY 0 [pol @ localhost inst] $ pero no entiendo lo que significa. La palabra "y" debe aparecer muchas veces en el archivo. ¿Qué estoy haciendo mal?
Vérace

1
Establecer intersección: sort set1 set2 | uniq -dno funciona para conjuntos múltiples. Considera usar sort <(sort -u set1) <(sort -u set2) | uniq -d.
neo

11

Algo así como. Debe lidiar con la clasificación usted mismo, pero commpuede usarse para hacer eso, tratando cada línea como un miembro fijo: -12por intersección, -13por diferencia. (Y -23le da una diferencia invertida, es decir, en set2 - set1lugar de set1 - set2). Union está sort -uen esta configuración.


1
De hecho, la comunicación parece hacer la mayoría de las cosas. Aunque los argumentos son muy poco intuitivos. ¡Gracias!
nilton

7

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', ...

Sí, buena respuesta. ¿Por qué usar awk si Python está disponible?
guettli

Olvidaste:Python> import os
James Bowery el

7

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_SUCCESSen caso de ser verdaderas, y EXIT_FAILUREtambié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 \fo 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 elementos

Ademá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.)
  • considerar elementos vacíos como válidos a través de --include-empty,
  • --ignore-case,
  • establece los --output-separatorelementos entre la secuencia de salida (el valor predeterminado es \n),
  • y así.

Visite man setopo github.com/phisigma/setop para obtener más información.


3

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, greppuede 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

2

Hice una utilidad Python que puede hacer unión, intersección, diferencia y producto de múltiples archivos en línea. Se llama SetOp, puedes encontrarlo en PyPI ( aquí ). La sintaxis se ve así:

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

1

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.


0

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 namey bucles o find, puede verificar los archivos existentes en dos o más directorios, para obtener la intersección o la diferencia.


1
Me refería a tratar el contenido de los archivos como los elementos de un conjunto (digamos, un elemento por línea), y los archivos mismos como conjuntos.
nilton

0

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.


0

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

0

Con zshmatrices (las zshmatrices pueden contener cualquier secuencia arbitraria de bytes, incluso 0).

(también tenga en cuenta que puede hacer typeset -U arraypara garantizar que sus elementos sean únicos).

establecer membresía

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

(usando el Iindicador de subíndice de matriz, para obtener el índice de la última aparición de $elementen la matriz (o 0 si no se encuentra). Elimine e(para exact) para $elementque 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 $~elementpara tomarlo como un patrón).

establecer intersección

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

${set1:*set2}hace la intersección de la matriz, pero la "${(@)...}"sintaxis es necesaria para preservar elementos vacíos

establecer la igualdad

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

Comprueba si las matrices son idénticas (y en el mismo orden). El qindicador 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 obandera para ordenarlos. Vea también la ubandera (única) para eliminar duplicados.

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

establecer cardinalidad

n=$#array

prueba de subconjunto

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

Unión

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

(ver typeset -Uarriba o el uindicador 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)

complemento

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

para los elementos de $array1que no están en $array2.

mínimo / máximo (comparación léxica)

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

mínimo / máximo (comparación de enteros decimales)

min=${${(no)array}[1]} max=${${(no)array}[-1]}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.