Supongamos que tiene un archivo que contiene direcciones IP, una dirección en cada línea:
10.0.10.1
10.0.10.1
10.0.10.3
10.0.10.2
10.0.10.1
Necesita un script de shell que cuente para cada dirección IP cuántas veces aparece en el archivo. Para la entrada anterior necesita la siguiente salida:
10.0.10.1 3
10.0.10.2 1
10.0.10.3 1
Una forma de hacer esto es:
cat ip_addresses |uniq |while read ip
do
echo -n $ip" "
grep -c $ip ip_addresses
done
Sin embargo, está muy lejos de ser eficiente.
¿Cómo resolverías este problema de manera más eficiente usando bash?
(Una cosa para agregar: sé que se puede resolver desde perl o awk, estoy interesado en una mejor solución en bash, no en esos idiomas).
INFORMACIÓN ADICIONAL:
Suponga que el archivo fuente es de 5 GB y que la máquina que ejecuta el algoritmo tiene 4 GB. Así que ordenar no es una solución eficiente, tampoco leer el archivo más de una vez.
Me gustó la solución similar a una tabla hash: ¿alguien puede proporcionar mejoras a esa solución?
INFORMACIÓN ADICIONAL # 2:
Algunas personas preguntaron por qué me molestaría hacerlo en bash cuando es mucho más fácil, por ejemplo, en Perl. La razón es que en la máquina tuve que hacer esto. Perl no estaba disponible para mí. Era una máquina Linux personalizada sin la mayoría de las herramientas a las que estoy acostumbrado. Y creo que fue un problema interesante.
Así que por favor, no culpes a la pregunta, simplemente ignórala si no te gusta. :-)
Respuestas:
Esto imprimirá el recuento primero, pero aparte de eso, debería ser exactamente lo que desea.
fuente
sort ip_addresses | uniq -c | sort -nr
sort ip_addresses | uniq -c | sort -nr | awk '{ print $2, $1 }'
para obtener la dirección IP en la primera columna y contar en la segunda.sort -nr -k1,1
El método rápido y sucio es el siguiente:
cat ip_addresses | sort -n | uniq -c
Si necesita usar los valores en bash, puede asignar todo el comando a una variable bash y luego recorrer los resultados.
PD
Si se omite el comando de clasificación, no obtendrá los resultados correctos, ya que uniq solo mira líneas idénticas sucesivas.
fuente
para resumir múltiples campos, en función de un grupo de campos existentes, use el siguiente ejemplo: (reemplace $ 1, $ 2, $ 3, $ 4 según sus requisitos)
fuente
sort
yuniq
son más fáciles para hacer recuentos, pero no ayudan cuando necesita calcular / sumar valores de campos. La sintaxis de matriz de awk es muy poderosa y clave para agrupar aquí. ¡Gracias!print
función de awk parece reducir los enteros de 64 bits a 32 bits, por lo que para valores int superiores a 2 ^ 31 es posible que desee usarprintf
con el%.0f
formato en lugar deprint
allíarr[$1,$2]+=$3+$4
por ejemplo, conarr[$1,$2]=(arr[$1,$2] $3 "," $4). I needed this to provide a grouped-by-package list of files (two columns only) and used:
arr [$ 1] = (arr [$ 1] $ 2) `con éxito.La solución canónica es la mencionada por otro encuestado:
Es más corto y conciso que lo que se puede escribir en Perl o awk.
Escribe que no desea utilizar la ordenación, porque el tamaño de los datos es mayor que el tamaño de la memoria principal de la máquina. No subestimes la calidad de implementación del comando de clasificación Unix. Sort se utilizó para manejar grandes volúmenes de datos (piense en los datos de facturación originales de AT&T) en máquinas con 128k (eso es 131,072 bytes) de memoria (PDP-11). Cuando la clasificación encuentra más datos que un límite preestablecido (a menudo ajustado cerca del tamaño de la memoria principal de la máquina), ordena los datos que ha leído en la memoria principal y los escribe en un archivo temporal. Luego repite la acción con los siguientes fragmentos de datos. Finalmente, realiza una ordenación por fusión en esos archivos intermedios. Esto permite que la ordenación funcione en datos muchas veces más grandes que la memoria principal de la máquina.
fuente
este comando le daría la salida deseada
fuente
Parece que tiene que usar una gran cantidad de código para simular hashes en bash para obtener un comportamiento lineal o apegarse a las versiones superlineales
cuadráticas.Entre esas versiones, la solución de saua es la mejor (y la más simple):
Encontré http://unix.derkeiler.com/Newsgroups/comp.unix.shell/2005-11/0118.html . Pero es feo como el infierno ...
fuente
Solución (agrupar por like mysql)
Resultado
fuente
Probablemente pueda usar el sistema de archivos como una tabla hash. Pseudocódigo de la siguiente manera:
Al final, todo lo que necesita hacer es atravesar todos los archivos e imprimir los nombres y números de los archivos en ellos. Alternativamente, en lugar de llevar un recuento, puede agregar un espacio o una nueva línea cada vez al archivo y, al final, simplemente mirar el tamaño del archivo en bytes.
fuente
Siento que una matriz asociativa awk también es útil en este caso
Un grupo por post aquí
fuente
La mayoría de las otras soluciones cuentan duplicados. Si realmente necesita agrupar pares de valores clave, intente esto:
Aquí están mis datos de ejemplo:
Esto imprimirá los pares de valores clave agrupados por la suma de comprobación md5.
fuente
Puro intento (¡sin tenedor!)
Hay una manera, usando un intentola función . ¡Este camino es muy rápido ya que no hay tenedor! ...
... ¡Mientras que las direcciones IP se mantienen pequeñas !
Nota: Las direcciones IP se convierten en un valor entero sin signo de 32 bits, que se usa como índice para la matriz . ¡Esto usa matrices bash simples , no matrices asociativas (lo cual es más costoso)!
En mi host, hacerlo es mucho más rápido que usar bifurcaciones, hasta aproximadamente 1'000 direcciones, pero me tomará aproximadamente 1 segundo entero cuando intente ordenar y contar 10'000 direcciones.
fuente
Lo hubiera hecho así:
pero uniq podría funcionar para ti.
fuente
Entiendo que está buscando algo en Bash, pero en caso de que alguien más esté buscando algo en Python, es posible que desee considerar esto:
Como los valores en el conjunto son únicos por defecto y Python es bastante bueno en estas cosas, puede ganar algo aquí. No he probado el código, por lo que podría tener errores, pero esto podría llevarte allí. Y si desea contar las ocurrencias, usar un dict en lugar de un conjunto es fácil de implementar.
Editar: Soy un pésimo lector, así que respondí mal. Aquí hay un fragmento con un dict que contaría las ocurrencias.
El diccionario mydict ahora contiene una lista de IP únicas como claves y la cantidad de veces que ocurrieron como sus valores.
fuente
itertools.groupby()
que combinada consorted()
hace exactamente lo que OP pide.La ordenación puede omitirse si el orden no es significativo
o
si la lista fuente es una variable
fuente