2010-04-23 37 views
112

Estoy tratando de escribir una secuencia de comandos simple que enumerará los contenidos que se encuentran en dos listas. Para simplificar, usemos ls como ejemplo. Imagina que "uno" y "dos" son directorios.Intersección de dos listas en Bash

 
one=`ls one` 
two=`ls two` 
intersection $one $two 

Todavía soy bastante verde en bash, así que no dude en corregir cómo estoy haciendo esto. Solo necesito un comando que imprima todos los archivos en "uno" y "dos". Deben existir en ambos. Puede llamar a esto la "intersección" entre "uno" y "dos".

+0

Nada aquí realmente responde la pregunta: cómo intersectar dos * variables * en un script Bash. – jameshfisher

+0

Parece una nueva pregunta en mi opinión, esa pregunta se responde claramente aquí. –

+0

Un enfoque posiblemente más útil es el casi duplicado http://stackoverflow.com/questions/2312762/compare-difference-of-two-arrays-in-bash – tripleee

Respuesta

182
comm -12 <(ls 1) <(ls 2) 
+22

No puedo creer que no tenía conocimiento de 'comm' hasta hoy. Esto acaba de hacer toda mi semana :) –

+11

'comm' requiere que las entradas sean ordenadas. En este caso, 'ls' ordena automáticamente su salida, pero otros usos pueden necesitar hacer esto:' comm -12 <(some-command | sort) <(some-other-command | sort) ' –

+7

NO USE ls ' salida para cualquier cosa. ls es una herramienta para buscar interactivamente los metadatos del directorio. Cualquier intento de analizar la salida de ls con el código está roto. Los Globs son mucho más simples Y correctos: '' para el archivo en * .txt ''. Lee http://mywiki.wooledge.org/ParsingLs –

27

utilizar el comando comm:

ls one | sort > /tmp/one_list 
ls two | sort > /tmp/two_list 
comm -12 /tmp/one_list /tmp/two_list 

"especie" no es realmente necesario, pero siempre lo incluyo antes de usar "comunicación" por si acaso.

+4

Es bueno incluirlo ya que no necesita ser ordenado , y él solo usó ls como ejemplo. – Thor84no

2

A (de comunicaciones) alternativa menos eficiente:

cat <(ls 1 | sort -u) <(ls 2 | sort -u) | uniq -d 
+1

Si está usando Debian's/bin/dash o algún otro shell que no sea Bash en sus secuencias de comandos, puede encadenar la salida de comandos utilizando paréntesis: '(ls 1; ls 2) | ordenar -u | uniq -d'. – nitrogen

+0

Usted está describiendo la unión, no la intersección. –

+1

@ MikaëlMayer Debería marcar el nombre de la persona usted está respondiendo, de lo contrario se supone que me quiere decir. – Benubird

27

solución con comm

comm es grande, pero de hecho hay que trabajar con la lista ordenada. Y, afortunadamente, aquí usamos ls que a partir de ls página del manual de Bash

Ordenar entradas alfabéticamente si ninguno de -cftuSUX ni --sort.

comm -12 <(ls one) <(ls two) 

alternativa con sort

intersección de dos listas:

sort <(ls one) <(ls two) | uniq -d 

diferencia simétrica de dos listas:

sort <(ls one) <(ls two) | uniq -u 

Bono

jugar con él;)

cd $(mktemp -d) && mkdir {one,two} && touch {one,two}/file_{1,2}{0..9} && touch two/file_3{0..9} 
+1

En lugar de * complemento *, creo que es lo que normalmente se llama * diferencia simétrica *. –

1

Ingreso es otra buena opción dependiendo de la entrada y la salida deseada

join -j1 -a1 <(ls 1) <(ls 2) 
-2

Hay otra pregunta Stackoverflow "intersección de matriz en bash" que está marcado como un duplicado de esto. No es lo mismo, en mi opinión, ya que esa pregunta habla de comparar dos arreglos bash, mientras que esta pregunta se centra en los archivos bash. Una respuesta de una línea a la otra pregunta, que ahora está cerrado, es como sigue:

# List1=(0 1 2 3 4 6 7 8 9 10 11 12) 
# List2=( 1 2 3 5 6 8 9 11) 
# List3=($(comm -12 <(echo ${List1[*]}| tr " " "\n"| sort) <(echo ${List2[*]} | tr " " "\n"| sort)| sort -g)) 
# echo ${List3[*]} 
1 2 3 6 8 9 11 

La utilidad de comunicaciones puede un tipo alfanumérico, mientras que la "matriz de intersección en bash" respuestas usar números; de ahí el uso de "ordenar" y "ordenar -g".