usa traduccion station rewards near shell

traduccion - ¿Comando de shell para sumar enteros, uno por línea?



shell usa price (30)

Alternativa a Perl puro, bastante legible, no se requieren paquetes ni opciones:

perl -e "map {$x += $_} <> and print $x" < infile.txt

Estoy buscando un comando que acepte como entrada múltiples líneas de texto, cada una de las cuales contiene un solo entero, y genera la suma de estos enteros.

Como parte de los antecedentes, tengo un archivo de registro que incluye mediciones de tiempo, así que a través de grepping para las líneas relevantes, y un poco de cambio de formato puedo enumerar todos los tiempos en ese archivo. Sin embargo, me gustaría calcular el total, y mi mente se ha quedado en blanco con respecto a cualquier comando al que pueda canalizar esta salida intermedia para poder hacer la suma final. Siempre he usado expr en el pasado, pero a menos que se ejecute en RPN mode no creo que vaya a hacer frente a esto (e incluso así sería complicado).

¿Qué me estoy perdiendo? Dado que probablemente haya varias formas de lograrlo, me complacerá leer (y upvote ) cualquier enfoque que funcione, incluso si alguien más ya ha publicado una solución diferente que hace el trabajo.

Pregunta relacionada: ¿ El comando más corto para calcular la suma de una columna de salida en Unix? (créditos @Andrew )

Actualización : Wow, como se esperaba, hay algunas respuestas bonitas aquí. ¡Parece que definitivamente tendré que realizar una inspección más profunda como una command-line tool en general!


C (no simplificado)

seq 1 10 | tcc -run <(cat << EOF #include <stdio.h> int main(int argc, char** argv) { int sum = 0; int i = 0; while(scanf("%d", &i) == 1) { sum = sum + i; } printf("%d/n", sum); return 0; } EOF)


C ++ (simplificado):

echo {1..10} | scc ''WRL n+=$0; n''

Proyecto SCC - http://volnitsky.com/project/scc/

SCC es un evaluador de fragmentos de C ++ en el indicador de shell


Con jq :

seq 10 | jq -s ''add'' # ''add'' is equivalent to ''reduce .[] as $item (0; . + $item)''


Creo que AWK es lo que estás buscando:

awk ''{sum+=$1}END{print sum}''

Puede usar este comando pasando la lista de números a través de la entrada estándar o pasando el archivo que contiene los números como parámetro.


Golpe puro y corto.

f=$(cat numbers.txt) echo $(( ${f//$''/n''/+} ))


Golpe simple

$ cat numbers.txt 1 2 3 4 5 6 7 8 9 10 $ sum=0; while read num; do ((sum += num)); done < numbers.txt; echo $sum 55


He hecho un rápido punto de referencia en las respuestas existentes que

  • usa solo herramientas estándar (perdón por cosas como lua o rocket ),
  • son reales de una sola línea,
  • son capaces de sumar grandes cantidades de números (100 millones), y
  • Son rápidos (ignoré los que tardaron más de un minuto).

Siempre agregué los números de 1 a 100 millones que se podían realizar en mi máquina en menos de un minuto para varias soluciones.

Aquí están los resultados:

Pitón

:; seq 100000000 | python -c ''import sys; print sum(map(int, sys.stdin))'' 5000000050000000 # 30s :; seq 100000000 | python -c ''import sys; print sum(int(s) for s in sys.stdin)'' 5000000050000000 # 38s :; seq 100000000 | python3 -c ''import sys; print(sum(int(s) for s in sys.stdin))'' 5000000050000000 # 27s :; seq 100000000 | python3 -c ''import sys; print(sum(map(int, sys.stdin)))'' 5000000050000000 # 22s :; seq 100000000 | pypy -c ''import sys; print(sum(map(int, sys.stdin)))'' 5000000050000000 # 11s :; seq 100000000 | pypy -c ''import sys; print(sum(int(s) for s in sys.stdin))'' 5000000050000000 # 11s

Awk

:; seq 100000000 | awk ''{s+=$1} END {print s}'' 5000000050000000 # 22s

Pegar y bc

Esto se quedó sin memoria en mi máquina. Funcionó para la mitad del tamaño de la entrada (50 millones de números):

:; seq 50000000 | paste -s -d+ - | bc 1250000025000000 # 17s :; seq 50000001 100000000 | paste -s -d+ - | bc 3750000025000000 # 18s

Así que supongo que habría tomado ~ 35s para los 100 millones de números.

Perl

:; seq 100000000 | perl -lne ''$x += $_; END { print $x; }'' 5000000050000000 # 15s :; seq 100000000 | perl -e ''map {$x += $_} <> and print $x'' 5000000050000000 # 48s

Rubí

:; seq 100000000 | ruby -e "puts ARGF.map(&:to_i).inject(&:+)" 5000000050000000 # 30s

do

Solo por comparación, compilé la versión C y probé esto también, solo para tener una idea de cuán lentas son las soluciones basadas en herramientas.

#include <stdio.h> int main(int argc, char** argv) { long sum = 0; long i = 0; while(scanf("%ld", &i) == 1) { sum = sum + i; } printf("%ld/n", sum); return 0; }

:; seq 100000000 | ./a.out 5000000050000000 # 8s

Conclusión

C es, por supuesto, el más rápido con 8s, pero la solución Pypy solo agrega una pequeña sobrecarga de alrededor del 30% a 11s . Pero, para ser justos, Pypy no es exactamente estándar. La mayoría de las personas solo tienen instalado CPython, que es significativamente más lento (22 s), exactamente tan rápido como la popular solución Awk.

La solución más rápida basada en herramientas estándar es Perl (15s).


La versión de una sola línea en Python:

$ python -c "import sys; print(sum(int(l) for l in sys.stdin))"


Lo siguiente debería funcionar (asumiendo que su número es el segundo campo en cada línea).

awk ''BEGIN {sum=0} / {sum=sum + $2} / END {print "tot:", sum}'' Yourinputfile.txt


Los siguientes trabajos en bash:

I=0 for N in `cat numbers.txt` do I=`expr $I + $N` done echo $I


Me doy cuenta de que esta es una pregunta antigua, pero me gusta esta solución lo suficiente como para compartirla.

% cat > numbers.txt 1 2 3 4 5 ^D % cat numbers.txt | perl -lpe ''$c+=$_}{$_=$c'' 15

Si hay interés, explicaré cómo funciona.


Mi version:

seq -5 10 | xargs printf "- - %s" | xargs | bc


Mis quince centavos

$ cat file.txt | xargs | sed -e ''s// /+/g'' | bc

Ejemplo:

$ cat text 1 2 3 3 4 5 6 78 9 0 1 2 3 4 576 7 4444 $ cat text | xargs | sed -e ''s// /+/g'' | bc 5148


One-liner en Racket:

racket -e ''(define (g) (define i (read)) (if (eof-object? i) empty (cons i (g)))) (foldr + 0 (g))'' < numlist.txt


Para Ruby Lovers

ruby -e "puts ARGF.map(&:to_i).inject(&:+)" numbers.txt


Pegar generalmente combina líneas de varios archivos, pero también se puede usar para convertir líneas individuales de un archivo en una sola línea. La bandera delimitadora le permite pasar una ecuación de tipo x + x a bc.

paste -s -d+ infile | bc

Alternativamente, cuando la tubería de stdin,

<commands> | paste -s -d+ - | bc


Plain Bash One Liner

$ cat > /tmp/test 1 2 3 4 5 ^D $ echo $(( $(cat /tmp/test | tr "/n" "+" ) 0 ))


Puede usar num-utils, aunque puede ser excesivo para lo que necesita. Este es un conjunto de programas para manipular números en el shell y puede hacer varias cosas ingeniosas, incluyendo, por supuesto, sumarlas. Está un poco desactualizado, pero aún funcionan y pueden ser útiles si necesita hacer algo más.

http://suso.suso.org/programs/num-utils/


Puede usar su comando ''expr'' preferido, solo necesita finagle un poco la entrada:

seq 10 | tr ''[/n]'' ''+'' | sed -e ''s/+/ + /g'' -e''s/ + $//n/'' | xargs expr

El proceso es:

  • "tr" reemplaza los caracteres eoln con un símbolo +,
  • sed rellena el ''+'' con espacios en cada lado, y luego elimina el final + de la línea
  • xargs inserta la entrada canalizada en la línea de comando para que expr la consuma.

Puedes hacerlo en python, si te sientes cómodo:

No probado, solo tecleado:

out = open("filename").read(); lines = out.split(''/n'') ints = map(int, lines) s = sum(ints) print s

Sebastián señaló un guión de una sola línea:

cat filename | python -c"from fileinput import input; print sum(map(int, input()))"


Puro bash y en una sola línea :-)

$ cat numbers.txt 1 2 3 4 5 6 7 8 9 10 $ I=0; for N in $(cat numbers.txt); do I=$(($I + $N)); done; echo $I 55


Solución BASH, si desea convertir esto en un comando (por ejemplo, si necesita hacerlo con frecuencia):

addnums () { local total=0 while read val; do (( total += val )) done echo $total }

Entonces uso:

addnums < /tmp/nums


Suma en tiempo real para permitirle monitorear el progreso de algunas tareas de procesamiento de números.

$ cat numbers.txt 1 2 3 4 5 6 7 8 9 10 $ cat numbers.txt | while read new; do total=$(($total + $new)); echo $total; done 1 3 6 10 15 21 28 36 45 55

(No es necesario establecer $total a cero en este caso. Tampoco puede acceder a $ total una vez finalizado).


Un poco de awk debería hacerlo?

awk ''{s+=$1} END {print s}'' mydatafile

Nota: algunas versiones de awk tienen algunos comportamientos extraños si va a agregar algo que exceda 2 ^ 31 (2147483647). Ver comentarios para más antecedentes. Una sugerencia es utilizar printf lugar de print :

awk ''{s+=$1} END {printf "%.0f", s}'' mydatafile


Yo pondría un gran AVISO en la solución comúnmente aprobada:

awk ''{s+=$1} END {print s}'' mydatafile # DO NOT USE THIS!!

esto se debe a que en esta forma, awk utiliza una representación de entero con signo de 32 bits: se desbordará para sumas que excedan 2147483647 (es decir, 2 ^ 31).

Una respuesta más general (para sumar enteros) sería:

awk ''{s+=$1} END {printf "%.0f/n", s}'' mydatafile # USE THIS INSTEAD

PD: Me hubiera gustado comentar la primera respuesta, pero no tengo suficiente reputación.


$ cat n 2 4 2 7 8 9

$ perl -MList::Util -le ''print List::Util::sum(<>)'' < n 32

O bien, puede escribir los números en la línea de comando:

$ perl -MList::Util -le ''print List::Util::sum(<>)'' 1 3 5 ^D 9

Sin embargo, este absorbe el archivo, por lo que no es una buena idea utilizarlo en archivos grandes. Vea la respuesta de j_random_hacker que evita los sorbos.


dc -f infile -e ''[+z1<r]srz1<rp''

Tenga en cuenta que los números negativos con el signo menos deben traducirse para dc , ya que usa el prefijo _ lugar del prefijo para eso. Por ejemplo, a través de tr ''-'' ''_'' | dc -f- -e ''...'' tr ''-'' ''_'' | dc -f- -e ''...'' .

Edit: Ya que esta respuesta obtuvo tantos votos "para la oscuridad", aquí hay una explicación detallada:

La expresión [+z1<r]srz1<rp hace lo siguiente :

[ interpret everything to the next ] as a string + push two values off the stack, add them and push the result z push the current stack depth 1 push one <r pop two values and execute register r if the original top-of-stack (1) is smaller ] end of the string, will push the whole thing to the stack sr pop a value (the string above) and store it in register r z push the current stack depth again 1 push 1 <r pop two values and execute register r if the original top-of-stack (1) is smaller p print the current top-of-stack

Como seudocódigo:

  1. Defina "add_top_of_stack" como:
    1. Quite los dos valores principales de la pila y agregue el resultado de nuevo
    2. Si la pila tiene dos o más valores, ejecute "add_top_of_stack" recursivamente
  2. Si la pila tiene dos o más valores, ejecute "add_top_of_stack"
  3. Imprime el resultado, ahora el único elemento que queda en la pila

Para entender realmente la simplicidad y el poder de dc , aquí hay un script de Python que implementa algunos de los comandos de dc y ejecuta una versión de Python del comando anterior:

### Implement some commands from dc registers = {''r'': None} stack = [] def add(): stack.append(stack.pop() + stack.pop()) def z(): stack.append(len(stack)) def less(reg): if stack.pop() < stack.pop(): registers[reg]() def store(reg): registers[reg] = stack.pop() def p(): print stack[-1] ### Python version of the dc command above # The equivalent to -f: read a file and push every line to the stack import fileinput for line in fileinput.input(): stack.append(int(line.strip())) def cmd(): add() z() stack.append(1) less(''r'') stack.append(cmd) store(''r'') z() stack.append(1) less(''r'') p()


perl -lne ''$x += $_; END { print $x; }'' < infile.txt


sed ''s/^/.+/'' infile | bc | tail -1