Fügen Sie in der Unix-Shell eine Zahlenspalte hinzu


197

Wenn files.txtich eine Liste der Dateien in habe , kann ich eine Liste ihrer Größen wie folgt erhalten:

cat files.txt | xargs ls -l | cut -c 23-30

was so etwas hervorbringt:

  151552
  319488
 1536000
  225280

Wie kann ich die Summe all dieser Zahlen ermitteln?

Antworten:


382
... | paste -sd+ - | bc

ist die kürzeste, die ich gefunden habe (aus dem UNIX Command Line- Blog).

Bearbeiten: Das -Argument für Portabilität wurde hinzugefügt , danke @Dogbert und @Owen.


Nett. Brauchen Sie die letzte - auch auf Solaris
Owen B

8
alias sum="paste -sd+ - | bc"zur Fertigstellung der Shell hinzugefügt, danke mate
slf

. . .| x=$(echo <(cat)); echo $((0+${x// /+}+0))Wenn Sie alle Bash die ganze Zeit wollen:
Qneill

13
@slf, pass auf, du bist gerade überladen/usr/bin/sum
qneill

3
Achtung, bcist auf einigen Systemen nicht verfügbar! awkAndererseits ist (glaube ich) für die POSIX-Konformität erforderlich.
vktec

154

Hier geht

cat files.txt | xargs ls -l | cut -c 23-30 | 
  awk '{total = total + $1}END{print total}'

34
Die Verwendung von awk ist eine gute Idee, aber warum sollte man das behalten cut? Das ist eine vorhersehbare Spaltennummer, also benutze... | xargs ls -l | awk '{total = total + $5}{END{print total}'
dmckee --- Ex-Moderator Kätzchen

3
Sie haben natürlich Recht - es war einfacher, nur an das Ende dessen anzuhängen, was bereits da war :-)
Greg Reynolds

2
Eine Klammer zu viel in @ dmckees Antwort :)
Dr. Jan-Philip Gehrcke

7
Um dies etwas kürzer zu machen, könnten Sie total+=$1anstelle vontotal = total + $1
vktec

10

Anstatt cut zu verwenden, um die Dateigröße aus der Ausgabe von ls -l zu ermitteln , können Sie direkt Folgendes verwenden:

$ cat files.txt | xargs ls -l | awk '{total += $5} END {print "Total:", total, "bytes"}'

Awk interpretiert "$ 5" als fünfte Spalte. Dies ist die Spalte von ls -l , in der die Dateigröße angegeben ist.


10

cat funktioniert nicht, wenn in Dateinamen Leerzeichen vorhanden sind. Hier ist stattdessen ein Perl-Einzeiler.

perl -nle 'chomp; $x+=(stat($_))[7]; END{print $x}' files.txt

8
python3 -c"import os; print(sum(os.path.getsize(f) for f in open('files.txt').read().split()))"

Oder wenn Sie nur die Zahlen summieren möchten, leiten Sie Folgendes ein:

python3 -c"import sys; print(sum(int(x) for x in sys.stdin))"

1
... | python -c'import sys; print(sum(int(x) for x in sys.stdin))'wenn Python 2 Ende dieses Jahres verschwindet.
Gleichnamig

don @ oysters: ~ / Dokumente $ cat tax | python3 -c "sys importieren; print (sum (int (x) für x in sys.stdin))" Traceback (letzter Aufruf zuletzt): Datei "<string>", Zeile 1, in <module> File "<string > ", Zeile 1, in <genexpr> ValueError: ungültiges Literal für int () mit Basis 10: '\ n'
hell


5

Das ganze ls -l und dann geschnitten ist ziemlich verworren, wenn Sie stat haben . Es ist auch anfällig für das genaue Format von ls -l (es hat nicht funktioniert, bis ich die Spaltennummern für cut geändert habe )

Auch die nutzlose Verwendung von Katze behoben .

<files.txt  xargs stat -c %s | paste -sd+ - | bc

2
Huh. Ich benutze Unix seit 32 Jahren und wusste nie, dass dies <infile commanddasselbe ist (und in einer besseren Reihenfolge als) command <infile.
Camille Goudeseune

5

Wenn Sie bc nicht installiert haben, versuchen Sie es

echo $(( $(... | paste -sd+ -) ))

anstatt

... | paste -sd+ - | bc

$( ) <- Gibt den Wert für die Ausführung des Befehls zurück

$(( 1+2 )) <- gibt die ausgewerteten Ergebnisse zurück

echo <- Echo auf dem Bildschirm


4

Sie können das folgende Skript verwenden, wenn Sie nur Shell-Skripte ohne awk oder andere Interpreter verwenden möchten:

#!/bin/bash

total=0

for number in `cat files.txt | xargs ls -l | cut -c 23-30`; do
   let total=$total+$number
done

echo $total

3

Ich würde stattdessen "du" verwenden.

$ cat files.txt | xargs du -c | tail -1
4480    total

Wenn Sie nur die Nummer wollen:

cat files.txt | xargs du -c | tail -1 | awk '{print $1}'

5
Festplattennutzung! = Dateigröße. du meldet die Datenträgernutzung.
0x6adb015

4
Ich denke, der Schalter -b bringt dich dazu, das zu tun, was ich brauche.
RichieHindle

@ 0x6adb015 Gute Kenntnisse. Danke, ich hatte es nicht bemerkt.
MichaelJones

3
Dies ist eine nützliche Antwort aus dem spezifischen Grund, warum das OP die Spalte mit den Zahlen hinzufügen wollte, aber für den allgemeinen Fall des Hinzufügens von Zahlen ist sie nicht ausreichend. (Ich benutze "du" die ganze Zeit selbst, aber ich bin hierher gekommen, um nach Kommandozeilenmathematik zu suchen. :-))
Michael H.

12
Dies funktioniert nicht, wenn files.txtes groß ist. Wenn die Anzahl der Argumente, xargsdie weitergeleitet werden, einen bestimmten Schwellenwert erreicht, werden sie über mehrere Aufrufe an aufgeteilt du. Die am Ende angezeigte Summe ist die Summe nur für den letzten Anruf bei du, nicht für die gesamte Liste.
Matthew Simoneau


1

Pipe to gawk:

 cat files.txt | xargs ls -l | cut -c 23-30 | gawk 'BEGIN { sum = 0 } // { sum = sum + $0 } END { print sum }'

1

Hier ist meins

cat files.txt | xargs ls -l | cut -c 23-30 | sed -e :a -e '$!N;s/\n/+/;ta' | bc

6
+1 für ein für alle Mal zu beweisen, dass es hässlichere Sprachen als Perl gibt :)
bdonlan

1
#
#       @(#) addup.sh 1.0 90/07/19
#
#       Copyright (C) <heh> SjB, 1990
#       Adds up a column (default=last) of numbers in a file.
#       95/05/16 updated to allow (999) negative style numbers.


case $1 in

-[0-9])

        COLUMN=`echo $1 | tr -d -`

        shift

;;

*)

        COLUMN="NF"

;;

esac

echo "Adding up column .. $COLUMN .. of file(s) .. $*"

nawk  ' OFMT="%.2f"                                       # 1 "%12.2f"

        { x = '$COLUMN'                                   # 2

          neg = index($x, "$")                            # 3

          if (neg > 0) X = gsub("\\$", "", $x)

          neg = index($x, ",")                            # 4

          if (neg > 1) X = gsub(",", "", $x)

          neg = index($x, "(")                            # 8 neg (123 & change

          if (neg > 0) X = gsub("\\(", "", $x)

          if (neg > 0) $x = (-1 * $x)                     # it to "-123.00"

          neg = index($x, "-")                            # 5

          if (neg > 1) $x = (-1 * $x)                     # 6

          t += $x                                         # 7

          print "x is <<<", $x+0, ">>> running balance:", t

        } ' $*


# 1.  set numeric format to eliminate rounding errors
# 1.1 had to reset numeric format from 12.2f to .2f 95/05/16
#     when a computed number is assigned to a variable ( $x = (-1 * $x) )
#     it causes $x to use the OFMT so -1.23 = "________-1.23" vs "-1.23"
#     and that causes my #5 (negative check) to not work correctly because
#     the index returns a number >1 and to the neg neg than becomes a positive
#     this only occurs if the number happened to b a "(" neg number
# 2.  find the field we want to add up (comes from the shell or defaults
#     to the last field "NF") in the file
# 3.  check for a dollar sign ($) in the number - if there get rid of it
#     so we may add it correctly - $12 $1$2 $1$2$ $$1$$2$$ all = 12
# 4.  check for a comma (,) in the number - if there get rid of it so we
#     may add it correctly - 1,2 12, 1,,2 1,,2,, all = 12   (,12=0)
# 5.  check for negative numbers
# 6.  if x is a negative number in the form 999- "make" it a recognized
#     number like -999 - if x is a negative number like -999 already
#     the test fails (y is not >1) and this "true" negative is not made
#     positive
# 7.  accumulate the total
# 8.  if x is a negative number in the form (999) "make it a recognized
#     number like -999
# * Note that a (-9) (neg neg number) returns a postive
# * Mite not work rite with all forms of all numbers using $-,+. etc. *

1

Ich benutze gerne ....

echo "
1
2
3 " | sed -e 's,$, + p,g' | dc 

Sie zeigen die Summe jeder Zeile ...

Anwendung über diese Situation:

ls -ld $(< file.txt) | awk '{print $5}' | sed -e 's,$, + p,g' | dc 

Total ist der letzte Wert ...


1
cat files.txt | awk '{ total += $1} END {print total}'

Sie können die awk verwenden, um dasselbe zu tun, und sogar die Nicht-Ganzzahlen überspringen

$ cat files.txt
1
2.3
3.4
ew
1

$ cat files.txt | awk '{ total += $1} END {print total}'
7.7

oder Sie können den Befehl ls verwenden und die vom Menschen lesbare Ausgabe berechnen

$ ls -l | awk '{ sum += $5} END  {hum[1024^3]="Gb"; hum[1024^2]="Mb"; hum[1024]="Kb"; for (x=1024^3; x>=1024; x/=1024) { if (sum>=x) { printf "%.2f %s\n",sum/x,hum[x]; break; } } if (sum<1024) print "1kb"; }'
15.69 Mb

$ ls -l *.txt | awk '{ sum += $5} END  {hum[1024^3]="Gb"; hum[1024^2]="Mb"; hum[1024]="Kb"; for (x=1024^3; x>=1024; x/=1024) { if (sum>=x) { printf "%.2f %s\n",sum/x,hum[x]; break; } } if (sum<1024) print "1kb"; }'
2.10 Mb

Sie brauchen nicht einmal Pfeife: awk '{ total += $1} END {print total}' files.txtist schneller
bmv

0

Meiner Meinung nach ist die einfachste Lösung dafür der Unix-Befehl "expr":

s=0; 
for i in `cat files.txt | xargs ls -l | cut -c 23-30`
do
   s=`expr $s + $i`
done
echo $s

0

Pure Bash

total=0; for i in $(cat files.txt | xargs ls -l | cut -c 23-30); do 
total=$(( $total + $i )); done; echo $total

0
sizes=( $(cat files.txt | xargs ls -l | cut -c 23-30) )
total=$(( $(IFS="+"; echo "${sizes[*]}") ))

Oder Sie können sie einfach zusammenfassen, während Sie die Größen lesen

declare -i total=0
while read x; total+=x; done < <( cat files.txt | xargs ls -l | cut -c 23-30 )

Wenn Sie sich nicht für Bissgrößen und Blöcke interessieren, ist das in Ordnung

declare -i total=0
while read s junk; total+=s; done < <( cat files.txt | xargs ls -s )

0

Wenn Sie R haben, können Sie verwenden:

> ... | Rscript -e 'print(sum(scan("stdin")));'
Read 4 items
[1] 2232320

Da ich mit R vertraut bin, habe ich tatsächlich mehrere Aliase für solche Dinge, damit ich sie verwenden kann, bashohne mich an diese Syntax erinnern zu müssen. Zum Beispiel:

alias Rsum=$'Rscript -e \'print(sum(scan("stdin")));\''

was lass mich machen

> ... | Rsum
Read 4 items
[1] 2232320

Inspiration: Gibt es eine Möglichkeit, Min, Max, Median und Durchschnitt einer Liste von Zahlen in einem einzigen Befehl zu ermitteln?

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.