Polecenie powłoki sumujące liczby całkowite, po jednym w wierszu?

866

Szukam polecenia, które zaakceptuje (jako dane wejściowe) wiele wierszy tekstu, z których każdy zawiera jedną liczbę całkowitą, i wyświetli sumę tych liczb całkowitych.

Jako trochę tła mam plik dziennika, który zawiera pomiary czasu. Poprzez grepping dla odpowiednich wierszy i trochę sedprzeformatowania mogę wymienić wszystkie czasy w tym pliku. Chciałbym obliczyć sumę. Mogę przesłać to wyjście pośrednie do dowolnego polecenia w celu dokonania ostatecznej sumy. Zawsze używałem exprw przeszłości, ale chyba, że ​​działa w trybie RPN, nie sądzę, że sobie z tym poradzi (i nawet wtedy byłoby to trudne).

Jak mogę uzyskać sumowanie liczb całkowitych?

Andrzej Doyle
źródło
2
Jest to bardzo podobne do pytania, które zadałem jakiś czas temu: stackoverflow.com/questions/295781/…
An̲̳̳drew
5
Naprawdę podoba mi się to pytanie, ponieważ istnieje wiele możliwych poprawnych (lub przynajmniej działających) odpowiedzi.
Francisco Canedo
To pytanie wydaje się być problemem dla golfa kodowego. codegolf.stackexchange.com :)
Gordon Bean

Odpowiedzi:

1321

Trochę awk powinien to zrobić?

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

Uwaga: niektóre wersje awk mają dziwne zachowania, jeśli zamierzasz dodać coś przekraczającego 2 ^ 31 (2147483647). Zobacz komentarze, aby uzyskać więcej informacji. Jedną z sugestii jest użycie printfzamiast print:

awk '{s+=$1} END {printf "%.0f", s}' mydatafile
Paul Dixon
źródło
7
W tym pokoju jest dużo niesamowitej miłości! Podoba mi się, jak taki prosty skrypt można zmodyfikować, aby dodać drugą kolumnę danych, zmieniając 1 $ na 2 $
Paul Dixon
2
Nie ma praktycznego limitu, ponieważ przetworzy dane wejściowe jako strumień. Jeśli więc może obsłużyć plik linii X, możesz być całkiem pewien, że poradzi sobie z X + 1.
Paul Dixon,
4
Kiedyś napisałem podstawowy procesor listy mailingowej ze skryptem awk uruchamianym za pomocą narzędzia urlopowego. Dobre czasy. :)
LS
2
właśnie użyłem tego do: zliczenia stron wszystkich dokumentów:ls $@ | xargs -i pdftk {} dump_data | grep NumberOfPages | awk '{s+=$2} END {print s}'
latających owiec
8
Uważaj, nie będzie działać z liczbami większymi niż 2147483647 (tj. 2 ^ 31), ponieważ awk używa 32-bitowej reprezentacji liczb całkowitych ze znakiem. Użyj awk '{s+=$1} END {printf "%.0f", s}' mydatafilezamiast tego.
Giancarlo Sportelli
665

Wklej zazwyczaj łączy wiersze wielu plików, ale można go również użyć do konwersji pojedynczych wierszy pliku w jeden wiersz. Flaga separatora pozwala przekazać równanie typu x + x do bc.

paste -s -d+ infile | bc

Alternatywnie, gdy wykonujesz orurowanie ze standardowego wejścia,

<commands> | paste -s -d+ - | bc
radoulov
źródło
1
Bardzo dobrze! Umieściłbym spację przed „+”, tylko po to, aby pomóc mi lepiej parsować, ale było to bardzo przydatne do przepuszczania niektórych numerów pamięci przez wklejanie, a następnie bc.
Michael H.
73
Znacznie łatwiejsze do zapamiętania i pisania niż rozwiązanie awk. Zauważ też, że pastemożna użyć myślnika -jako nazwy pliku - co pozwoli ci potokować liczby z wyjścia polecenia do standardowego wyjścia wklejania bez potrzeby uprzedniego utworzenia pliku:<commands> | paste -sd+ - | bc
George
19
Mam plik ze 100 milionami numerów. Polecenie awk zajmuje 21 sekund; polecenie wklejenia zajmuje 41 sekund. Niemniej jednak dobrze jest poznać „wklej”!
Abhi
4
@Abhi: Interesujące: Wydaje mi się, że zajęłoby mi 20 sekund, aby wymyślić polecenie awk, więc wyrównuje się, dopóki nie spróbuję 100 milionów i jednej liczby: D
Mark K Cowan
6
@George Możesz jednak pominąć -. (Jest to przydatne, jeśli chcesz połączyć plik ze standardowym wejściem).
Alois Mahdal
128

Wersja jednoliniowa w Pythonie:

$ python -c "import sys; print(sum(int(l) for l in sys.stdin))"
dF.
źródło
Powyższy jeden wiersz nie działa dla plików w sys.argv [], ale ten działa stackoverflow.com/questions/450799/…
jfs
To prawda, autor powiedział, że zamierza przesłać dane wyjściowe z innego skryptu do polecenia, a ja starałem się, aby był jak najkrótszy :)
dF.
39
Krótsza wersja byłabypython -c"import sys; print(sum(map(int, sys.stdin)))"
jfs
4
Uwielbiam tę odpowiedź za łatwość czytania i elastyczność. Potrzebowałem średniego rozmiaru plików mniejszych niż 10 Mb w kolekcji katalogów i zmodyfikowałem go w ten sposób:find . -name '*.epub' -exec stat -c %s '{}' \; | python -c "import sys; nums = [int(n) for n in sys.stdin if int(n) < 10000000]; print(sum(nums)/len(nums))"
Paul Whipp
1
Możesz również odfiltrować liczby import sys; print(sum(int(''.join(c for c in l if c.isdigit())) for l in sys.stdin))
nieparzyste,
91

Złożyłbym duże OSTRZEŻENIE na powszechnie akceptowane rozwiązanie:

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

to dlatego, że w tej formie awk używa 32-bitowej reprezentacji liczb całkowitych ze znakiem: przepełni się dla sum przekraczających 2147483647 (tj. 2 ^ 31).

Bardziej ogólną odpowiedzią (dla sumowania liczb całkowitych) byłoby:

awk '{s+=$1} END {printf "%.0f\n", s}' mydatafile # USE THIS INSTEAD
Giancarlo Sportelli
źródło
Dlaczego printf () pomaga tutaj? Przepełnienie int miało miejsce wcześniej, ponieważ kod sumowania jest taki sam.
Robert Klemme,
9
Ponieważ problem tkwi w funkcji „drukowania”. Awk używa 64-bitowych liczb całkowitych, ale z jakiegoś powodu print donw skaluje je do 32-bitowych.
Giancarlo Sportelli,
4
Błąd drukowania wydaje się być naprawiony, przynajmniej dla awk 4.0.1 i bash 4.3.11, chyba że się mylę: echo -e "2147483647 \n 100" |awk '{s+=$1}END{print s}'pokazuje2147483747
Xen2050
4
Używanie pływaków po prostu wprowadza nowy problem: echo 999999999999999999 | awk '{s+=$1} END {printf "%.0f\n", s}'produkuje1000000000000000000
Patrick
1
Czy nie powinno się po prostu używać „% ld” w systemach 64-bitowych, aby nie skracać printf do 32-bitów? Jak wskazuje @Patrick, floaty nie są tutaj dobrym pomysłem.
yerforkferchips
78

Zwykły bash:

$ 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
Giacomo
źródło
2
Mniejszy jeden linijka: stackoverflow.com/questions/450799/…
Khaja Minhajuddin
@rjack, gdzie jest numzdefiniowany? Wierzę, że jest to jakoś związane z < numbers.txtwyrażeniem, ale nie jest jasne, w jaki sposób.
Atcold
66
dc -f infile -e '[+z1<r]srz1<rp'

Zauważ, że liczby ujemne poprzedzone znakiem minus powinny zostać przetłumaczone dc, ponieważ używa do tego _raczej -prefiksu niż prefiksu. Na przykład przez tr '-' '_' | dc -f- -e '...'.

Edycja: Ponieważ ta odpowiedź uzyskała tyle głosów „za niejasnością”, oto szczegółowe wyjaśnienie:

Wyrażenie [+z1<r]srz1<rp wykonuje następujące czynności :

[   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

Jako pseudo-kod:

  1. Zdefiniuj „add_top_of_stack” jako:
    1. Usuń dwie najwyższe wartości ze stosu i dodaj wynik z powrotem
    2. Jeśli stos ma dwie lub więcej wartości, uruchom rekurencyjnie polecenie „add_top_of_stack”
  2. Jeśli stos ma dwie lub więcej wartości, uruchom polecenie „add_top_of_stack”
  3. Wydrukuj wynik, teraz jedyny element pozostały na stosie

Aby naprawdę zrozumieć prostotę i moc dc, oto działający skrypt Pythona, który implementuje niektóre polecenia z dci wykonuje wersję powyższego polecenia w języku Python:

### 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()
André Laszlo
źródło
2
dc to tylko wybrane narzędzie do użycia. Ale zrobiłbym to z nieco mniejszą liczbą stosów. Zakłada się, że wszystkie linie naprawdę zawierają szereg: (echo "0"; sed 's/$/ +/' inp; echo 'pq')|dc.
ikrabbe,
5
Algorytm stronie: dc -e '0 0 [+?z1<m]dsmxp'. Dlatego nie zapisujemy wszystkich liczb na stosie przed przetwarzaniem, ale odczytujemy i przetwarzamy je jeden po drugim (a dokładniej, linia po linii, ponieważ jedna linia może zawierać kilka liczb). Zauważ, że pusty wiersz może zakończyć sekwencję wejściową.
ruvim
@ikrabbe to świetnie. Można go skrócić o jeszcze jeden znak: spację w sedpodstawieniu można usunąć, ponieważ dc nie ma znaczenia spacje między argumentami a operatorami. (echo "0"; sed 's/$/+/' inputFile; echo 'pq')|dc
WhiteHotLoveTiger
58

Z jq :

seq 10 | jq -s 'add' # 'add' is equivalent to 'reduce .[] as $item (0; . + $item)'
Banyan
źródło
7
Podoba mi się, ponieważ wydaje mi się, że jest tak jasny i krótki, że mógłbym go zapamiętać.
Alfe
46

Czysty i krótki bash.

f=$(cat numbers.txt)
echo $(( ${f//$'\n'/+} ))
Daniel
źródło
9
Jest to najlepsze rozwiązanie, ponieważ nie tworzy żadnego podprocesu, jeśli zastąpisz pierwszą linię f=$(<numbers.txt).
loentar
1
jakikolwiek sposób na wejście ze standardowego wejścia? jak z fajki?
njzk2
@ njzk2 Jeśli wstawisz f=$(cat); echo $(( ${f//$'\n'/+} ))skrypt, możesz potokować wszystko do tego skryptu lub wywoływać go bez argumentów na interaktywne wejście standardowego wejścia (zakończ za pomocą Control-D).
mklement0
5
@loentar <numbers.txtJest to ulepszenie, ale ogólnie rzecz biorąc, to rozwiązanie jest skuteczne tylko w przypadku małych plików wejściowych; na przykład przy pliku 1000 wierszy wejściowych zaakceptowane awkrozwiązanie jest około 20 razy szybsze na moim komputerze - a także zużywa mniej pamięci, ponieważ plik nie jest odczytywany jednocześnie.
mklement0
2
Prawie straciłem nadzieję, kiedy do niego dotarłem. Pure Bash!
Omer Akhter
37
perl -lne '$x += $_; END { print $x; }' < infile.txt
j_random_hacker
źródło
4
I dodałem je z powrotem: „-l” zapewnia, że ​​wyjście jest zakończone LF, tak jak `` backticks powłoki i większość programów się spodziewa, a '<' oznacza, że ​​tego polecenia można użyć w potoku.
j_random_hacker
Masz rację. Jako wymówka: Każda postać w liniowcach Perla wymaga ode mnie pracy umysłowej, dlatego wolę rozebrać jak najwięcej postaci. W tym przypadku nawyk był szkodliwy.
jfs
2
Jedno z niewielu rozwiązań, które nie ładuje wszystkiego do pamięci RAM.
Erik Aronesty,
28

Moje piętnaście centów:

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

Przykład:

$ 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
niewinny świat
źródło
Moje dane wejściowe mogą zawierać puste wiersze, więc użyłem tego, co tu opublikowałeś, a także grep -v '^$'. Dzięki!
James Oravec
łał!! twoja odpowiedź jest niesamowita! mój osobisty faworyt ze wszystkich w bieżniku
thahgr
Uwielbiam to i +1 za potok. Bardzo proste i łatwe dla mnie rozwiązanie
Gelin Luo
24

Zrobiłem szybki test porównawczy na istniejące odpowiedzi, które

  • używaj tylko standardowych narzędzi (przepraszam za rzeczy takie jak lualubrocket ),
  • są prawdziwymi liniowcami,
  • są w stanie dodać ogromne ilości liczb (100 milionów), oraz
  • są szybkie (zignorowałem te, które trwały dłużej niż minutę).

Zawsze dodawałem liczby od 1 do 100 milionów, które były możliwe do wykonania na mojej maszynie w mniej niż minutę dla kilku rozwiązań.

Oto wyniki:

Pyton

:; 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

Wklej i Bc

Na mojej maszynie zabrakło pamięci. To działało dla połowy wielkości wejściowej (50 milionów liczb):

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

Sądzę więc, że zajęłoby to około 35 sekund dla 100 milionów liczb.

Perl

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

Rubin

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

do

Dla porównania, skompilowałem wersję C i przetestowałem ją również, aby zorientować się, o ile wolniejsze są rozwiązania oparte na narzędziach.

#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

Wniosek

C jest oczywiście najszybszy z 8s, ale rozwiązanie Pypy dodaje tylko niewielki narzut około 30% do 11s . Ale, szczerze mówiąc, Pypy nie jest dokładnie standardem. Większość ludzi ma zainstalowany tylko CPython, który jest znacznie wolniejszy (22 s), dokładnie tak szybko, jak popularne rozwiązanie Awk.

Najszybszym rozwiązaniem opartym na standardowych narzędziach jest Perl (15s).

Alfe
źródło
2
paste+ bcPodejście zostało tylko to, co szukałem do sumy wartości hex, dzięki!
Tomislav Nakic-Alfirevic
1
Dla zabawy w Rust:use std::io::{self, BufRead}; fn main() { let stdin = io::stdin(); let mut sum: i64 = 0; for line in stdin.lock().lines() { sum += line.unwrap().parse::<i64>().unwrap(); } println!("{}", sum); }
Jocelyn
niesamowita odpowiedź. nie, aby podrywać, ale jest tak, że jeśli zdecydujesz się uwzględnić te dłuższe wyniki, odpowiedź byłaby jeszcze bardziej niesamowita!
Steven Lu
@StevenLu Czułem, że odpowiedź byłaby dłuższa, a przez to mniej niesamowita (żeby użyć twoich słów). Ale rozumiem, że nie wszyscy muszą odczuwać to uczucie :)
Alfe
Dalej: numba + równoległość
gerrit
17

Prosta podszewka z jedną wkładką

$ cat > /tmp/test
1 
2 
3 
4 
5
^D

$ echo $(( $(cat /tmp/test | tr "\n" "+" ) 0 ))
Khaja Minhajuddin
źródło
7
Nr kat potrzebne: echo $(( $( tr "\n" "+" < /tmp/test) 0 ))
AGC
2
trnie jest dokładnie „zwykłym uderzeniem” / nitpick
Benjamin W.
17

Rozwiązanie BASH, jeśli chcesz ustawić to jako polecenie (np. Jeśli musisz to robić często):

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

Następnie użycie:

addnums < /tmp/nums
Jay
źródło
14

Myślę, że AWK jest tym, czego szukasz:

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

Możesz użyć tego polecenia albo przez przekazanie listy liczb przez standardowe wejście, albo przez przekazanie pliku zawierającego liczby jako parametru.

Paolo
źródło
11

Następujące działa w bash:

I=0

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

echo $I
Francisco Canedo
źródło
1
Rozwijania poleceń należy używać ostrożnie, gdy pliki mogą być dowolnie duże. Przy liczbach.txt 10 MB cat numbers.txtkrok byłby problematyczny.
Giacomo
1
Rzeczywiście jednak (gdyby nie lepsze znalezione tu rozwiązania) używałbym tego, dopóki nie napotkałem tego problemu.
Francisco Canedo
11

Możesz używać num-utils, chociaż może to być przesada w stosunku do tego, czego potrzebujesz. Jest to zestaw programów do manipulowania liczbami w powłoce i może robić kilka fajnych rzeczy, w tym oczywiście dodawać je. To trochę nieaktualne, ale nadal działają i mogą być przydatne, jeśli chcesz zrobić coś więcej.

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

sykora
źródło
Przykład: numsum numbers.txt.
agc
9

Zdaję sobie sprawę, że to stare pytanie, ale podoba mi się to rozwiązanie, aby je udostępnić.

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

Jeśli jest zainteresowanie, wyjaśnię, jak to działa.

Nym
źródło
10
Proszę nie. Lubimy udawać, że -n i -p to ładne semantyczne rzeczy, a nie tylko sprytne wklejanie strun;)
hobbs
2
Tak, proszę wyjaśnij :) (Nie jestem typem Perla).
Jens
1
Spróbuj uruchomić „perl -MO = Deparse -lpe '$ c + = $ _} {$ _ = $ c'” i patrząc na wynik, w zasadzie -l używa znaków nowej linii oraz separatorów wejścia i wyjścia, a -p drukuje każdą linię. Ale aby zrobić „-p”, perl najpierw dodaje płytę kotła (która -MO = Deparse) pokaże, ale potem po prostu podstawia i kompiluje. W ten sposób możesz spowodować wstawienie dodatkowego bloku z częścią „} {” i nakłonić go, aby nie drukował na każdej linii, ale drukował na samym końcu.
Nym
9

Pure Bash w jednej linijce :-)

$ 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
Oliver Ertl
źródło
Dlaczego są dwa ((nawiasy ))?
Atcold
Niezupełnie czysty bash z powodu kota. zrób z tego czystą $(< numbers.txt)
walkę,
9
sed 's/^/.+/' infile | bc | tail -1
Dominique
źródło
6

Alternatywny czysty Perl, dość czytelny, nie wymaga żadnych pakietów ani opcji:

perl -e "map {$x += $_} <> and print $x" < infile.txt
clint
źródło
lub nieco krótszy: perl -e 'map {$ x + = $ _} <>; print $ x 'infile.txt
Avi
Wymagana pamięć to prawie 2 GB dla dużego wejścia 10 milionów liczb
Amit Naidu
6

Dla miłośników rubinów

ruby -e "puts ARGF.map(&:to_i).inject(&:+)" numbers.txt
johnlinvc
źródło
5

Nie można uniknąć przesłania tego:

jot 1000000 | sed '2,$s/$/+/;$s/$/p/' | dc

Można go znaleźć tutaj:
najbardziej elegancki jednowarstwowy płaszcz unixowy do sumowania liczb o dowolnej precyzji?

A oto jego szczególne zalety w stosunku do awk, bc i przyjaciół:

  • nie zależy od buforowania, a zatem nie dusi się przy naprawdę dużych wejściach
  • nie implikuje żadnej szczególnej precyzji ani wielkości całkowitych dla tego limitu materii
  • nie ma potrzeby stosowania innego kodu, jeśli trzeba dodać liczby zmiennoprzecinkowe
fgeorgatos
źródło
W odpowiedzi prosimy podać kod związany z pytaniem i nie odwoływać się do linku
Ibo
5

Korzystanie z GNU datamash util :

seq 10 | datamash sum 1

Wynik:

55

Jeśli dane wejściowe są nieregularne, ze spacjami i tabulatorami w nieparzystych miejscach, może to mylić datamash, a następnie użyj -Wprzełącznika:

<commands...> | datamash -W sum 1

... lub użyj trdo wyczyszczenia białych znaków:

<commands...> | tr -d '[[:blank:]]' | datamash sum 1
agc
źródło
4

Moja wersja:

seq -5 10 | xargs printf "- - %s" | xargs  | bc
Vytenis Bivainis
źródło
2
Krótszy:seq -s+ -5 10 | bc
agc 11.04.16
3

Możesz to zrobić w pythonie, jeśli czujesz się komfortowo:

Nie testowane, po prostu wpisane:

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

Sebastian wskazał jeden skrypt liniowy:

cat filename | python -c"from fileinput import input; print sum(map(int, input()))"
Tiago
źródło
python -c "z wejścia importu pliku; wydrukuj sumę (map (int, input ()))" numbers.txt
jfs
2
kot jest nadużywany, przekieruj standardowe wejście z pliku: python -c "..." <numbers.txt
Giacomo
2
@rjack: catsłuży do wykazania, że ​​skrypt działa zarówno dla standardowego wejścia, jak i dla plików w argv [] (jak while(<>)w Perlu). Jeśli dane wejściowe znajdują się w pliku, wówczas „<” nie jest konieczne.
jfs
2
Ale < numbers.txtpokazuje, że działa tak samo dobrze, jak standardowe cat numbers.txt |. I nie uczy złych nawyków.
Xiong Chiamiov
3
$ cat n
2)
4
2)
7
8
9
$ perl -MList::Util -le 'print List::Util::sum(<>)' < n
32

Lub możesz wpisać liczby w wierszu poleceń:

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

Jednak ten plik sluruje plik, więc nie jest dobrym pomysłem stosowanie do dużych plików. Zobacz odpowiedź j_random_hackera, która pozwala uniknąć osiadania.

Sinan Ünür
źródło
3

Poniższe powinno działać (zakładając, że Twój numer jest drugim polem w każdej linii).

awk 'BEGIN {sum=0} \
 {sum=sum + $2} \
END {print "tot:", sum}' Yourinputfile.txt
James Anderson
źródło
2
Naprawdę nie potrzebujesz części {sum = 0}
Uphill_ What '1
3

One-liner in Racket:

racket -e '(define (g) (define i (read)) (if (eof-object? i) empty (cons i (g)))) (foldr + 0 (g))' < numlist.txt
b2coutts
źródło
3

C (nie uproszczony)

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)
Greg Bowyer
źródło
Musiałem głosować za komentarzem. Nie ma nic złego w odpowiedzi - jest całkiem dobra. Jednak, aby pokazać, że komentarz sprawia, że ​​odpowiedź jest niesamowita, po prostu oceniam komentarz.
bballdave025
3

Z góry przepraszamy za czytelność tylnych kresek („'”), ale działają one w powłokach innych niż bash, a zatem są łatwiejsze do wklejenia. Jeśli używasz powłoki, która ją akceptuje, format $ (polecenie ...) jest znacznie bardziej czytelny (a przez to debugowalny) niż `polecenie ... ', więc możesz modyfikować dla własnego zdrowia psychicznego.

Mam prostą funkcję w moim bashrc, która użyje awk do obliczenia liczby prostych elementów matematycznych

calc(){
  awk 'BEGIN{print '"$@"' }'
}

To zrobi +, -, *, /, ^,%, sqrt, sin, cos, nawias ... (i więcej w zależności od twojej wersji awk) ... możesz nawet się spodobać z printf i formatem zmiennoprzecinkowym wyjście, ale to wszystko, czego zwykle potrzebuję

w przypadku tego konkretnego pytania po prostu zrobiłbym to dla każdej linii:

calc `echo "$@"|tr " " "+"`

więc blok kodu sumujący każdą linię wyglądałby mniej więcej tak:

while read LINE || [ "$LINE" ]; do
  calc `echo "$LINE"|tr " " "+"` #you may want to filter out some lines with a case statement here
done

To jest, jeśli chcesz sumować je tylko linia po linii. Jednak w sumie dla każdej liczby w pliku danych

VARS=`<datafile`
calc `echo ${VARS// /+}`

btw, jeśli muszę zrobić coś szybko na pulpicie, używam tego:

xcalc() { 
  A=`calc "$@"`
  A=`Xdialog --stdout --inputbox "Simple calculator" 0 0 $A`
  [ $A ] && xcalc $A
}
technozaura
źródło
2
Jakiej starożytnej powłoki używasz, która nie obsługuje $()?
nyuszika7h