Question Commande Shell pour additionner des entiers, un par ligne?


Je suis à la recherche d'une commande qui acceptera en entrée plusieurs lignes de texte, chaque ligne contenant un seul entier, et affichera la somme de ces entiers.

Comme un peu d'arrière-plan, j'ai un fichier journal qui comprend des mesures de synchronisation, donc par grepping pour les lignes pertinentes, et un peu de sed reformatage je peux énumérer tous les timings dans ce dossier. Je voudrais travailler le total cependant, et mon esprit est devenu vide quant à n'importe quelle commande que je peux diriger cette sortie intermédiaire afin de faire la somme finale. J'ai toujours utilisé expr dans le passé, mais à moins qu'il ne fonctionne RPN mode Je ne pense pas que ça va faire face à cela (et même alors, ce serait difficile).

Qu'est-ce que je rate? Étant donné qu'il y a probablement plusieurs façons d'y parvenir, je serai heureux de lire (et upvote) toute approche qui fonctionne, même si quelqu'un d'autre a déjà posté une solution différente qui fait le travail.

Question connexe: Commande la plus courte pour calculer la somme d'une colonne de sortie sous Unix?  (crédits @Andrew)


Mettre à jour: Wow, comme prévu, il y a quelques bonnes réponses ici. On dirait que je vais certainement devoir donner awk inspection plus approfondie en tant que command-line tool en général!


683


origine


Réponses:


Un peu d'awk devrait le faire?

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

Note: certaines versions d'awk ont ​​des comportements bizarres si vous allez ajouter quelque chose dépassant 2 ^ 31 (2147483647). Voir les commentaires pour plus d'informations. Une suggestion est d'utiliser printf plutôt que print:

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

1084



Coller fusionne généralement des lignes de plusieurs fichiers, mais il peut également être utilisé pour convertir des lignes individuelles d'un fichier en une seule ligne. L'indicateur delimiter vous permet de passer une équation de type x + x à bc.

paste -s -d+ infile | bc

Alternativement, lors de la tuyauterie de stdin,

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

571



La version one-liner en Python:

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

101



Bash plain:

$ 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

68



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

Notez que les nombres négatifs précédés d'un signe moins doivent être traduits dc, car il utilise _ préfixe plutôt que - préfixe pour cela. Par exemple, via tr '-' '_' | dc -f- -e '...'.

Edit: Puisque cette réponse a eu autant de votes "pour l'obscurité", voici une explication détaillée:

L'expression [+z1<r]srz1<rp  fait ce qui suit:

[   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

Comme pseudo-code:

  1. Définir "add_top_of_stack" comme:
    1. Supprimez les deux valeurs supérieures de la pile et ajoutez le résultat
    2. Si la pile a deux valeurs ou plus, exécutez récursivement "add_top_of_stack"
  2. Si la pile a deux valeurs ou plus, exécutez "add_top_of_stack"
  3. Imprime le résultat, maintenant le seul élément restant dans la pile

Pour vraiment comprendre la simplicité et la puissance de dc, voici un script Python qui implémente certaines des commandes de dc et exécute une version Python de la commande ci-dessus:

### 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()

60



Je mettrais un gros avertissement sur la solution communément approuvée:

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

c'est parce que dans cette forme awk utilise une représentation entière signée de 32 bits: elle débordera pour des sommes qui dépassent 2147483647 (c'est-à-dire, 2 ^ 31).

Une réponse plus générale (pour additionner des entiers) serait:

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

P.S. J'aurais aimé commenter la première réponse, mais je n'ai pas assez de réputation ..


60



Bash pur et court.

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

40



Avec jq:

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

40



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

36