เพิ่มคอลัมน์ของตัวเลขที่เชลล์ Unix


198

เมื่อรับรายการไฟล์files.txtฉันจะได้รายการขนาดดังนี้:

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

ซึ่งผลิตบางสิ่งเช่นนี้

  151552
  319488
 1536000
  225280

ฉันจะหาจำนวนทั้งหมดเหล่านั้นได้อย่างไร

คำตอบ:


383
... | paste -sd+ - | bc

เป็นหนึ่งที่สั้นที่สุดที่ฉันพบ (จากบล็อกบรรทัดคำสั่งของ UNIX )

แก้ไข:เพิ่ม-อาร์กิวเมนต์สำหรับการพกพาขอบคุณ @Dogbert และ @Owen


ดี ต้องการโซลาริสรุ่นสุดท้ายด้วย
โอเวนบี

8
alias sum="paste -sd+ - | bc"เพิ่มลงในเชลล์เสร็จแล้วขอบคุณเพื่อน
slf

. . .| x=$(echo <(cat)); echo $((0+${x// /+}+0))ถ้าคุณต้องการทุบตีทั้งหมดตลอดเวลา:
qneill

13
@slf ระวังคุณเพิ่งโอเวอร์โหลด/usr/bin/sum
qneill

3
ระวังbcไม่มีในบางระบบ! awkในทางกลับกัน (ฉันเชื่อว่า) เป็นสิ่งจำเป็นสำหรับการปฏิบัติตาม POSIX
vktec

154

นี่ไง

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

34
ใช้ awk เป็นความคิดที่ดี แต่ทำไมให้cut? นั่นคือหมายเลขคอลัมน์ที่สามารถคาดเดาได้ดังนั้นให้ใช้... | xargs ls -l | awk '{total = total + $5}{END{print total}'
dmckee --- ผู้ดูแลอดีตลูกแมว

3
คุณถูกต้องแน่นอน - มันก็ง่ายเพียงเพื่อผนวกเกี่ยวกับการสิ้นสุดของสิ่งที่มีอยู่แล้ว :-)
เกร็กนาดส์

2
วงเล็บหนึ่งตัวมากเกินไปในคำตอบของ
@dmckee

7
เพื่อให้สั้นลงเล็กน้อยคุณสามารถใช้total+=$1แทนtotal = total + $1
vktec

10

แทนที่จะใช้การตัดเพื่อให้ได้ขนาดไฟล์จากเอาต์พุตของls -lคุณสามารถใช้โดยตรง:

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

Awk ตีความ "$ 5" เป็นคอลัมน์ที่ห้า นี่คือคอลัมน์จากls -lที่ให้ขนาดไฟล์แก่คุณ


10

แมวจะไม่ทำงานหากมีช่องว่างในชื่อไฟล์ นี่คือ perl one-liner แทน

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

หรือถ้าคุณเพียงต้องการหาผลรวมของตัวเลข, ท่อเข้าไปใน:

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))'เมื่อ python 2 หายไปเมื่อสิ้นปีนี้
Eponymous

don @ oysters: ~ / เอกสาร $ cat tax | python3 -c "import sys; print (sum (int (x) สำหรับ x ใน sys.stdin))" Traceback (การโทรล่าสุดครั้งล่าสุด): ไฟล์ "<string>", บรรทัด 1, ใน <module> ไฟล์ "<string > ", บรรทัด 1, ใน <genexpr> ValueError: ตัวอักษรที่ไม่ถูกต้องสำหรับ int () พร้อมฐาน 10: '\ n'
ดอนสดใส


5

แอลเอสทั้ง -l แล้วตัดซับซ้อนค่อนข้างเมื่อคุณมีสถิติ นอกจากนี้ยังเสี่ยงต่อรูปแบบที่แน่นอนของls -l (มันไม่ทำงานจนกว่าฉันจะเปลี่ยนหมายเลขคอลัมน์สำหรับการตัด )

นอกจากนี้ยังคงใช้งานที่ไร้ประโยชน์ของแมว

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

2
ฮะ. ได้ใช้ระบบปฏิบัติการยูนิกซ์สำหรับ 32 ปีและไม่เคยรู้ว่า<infile commandเป็นเช่นเดียวกับ command <infile(และในลำดับที่ดีกว่า)
Camille Goudeseune

5

หากคุณไม่ได้ติดตั้ง bc ให้ลอง

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

แทน

... | paste -sd+ - | bc

$( ) <- ส่งคืนค่าของการดำเนินการคำสั่ง

$(( 1+2 )) <- ส่งคืนผลลัพธ์ที่ประเมิน

echo <- echo ไปที่หน้าจอ


4

คุณสามารถใช้สคริปต์ต่อไปนี้หากคุณต้องการใช้เชลล์สคริปต์โดยไม่ต้อง awk หรือล่ามอื่น ๆ :

#!/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

ฉันจะใช้ "du" แทน

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

หากคุณต้องการหมายเลข:

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

5
การใช้งานดิสก์! = ขนาดของไฟล์ คุณรายงานการใช้งานดิสก์
0x6adb015

4
ฉันคิดว่าสวิตช์ -b ทำให้ du ทำในสิ่งที่ฉันต้องการ
RichieHindle

@ 0x6adb015 ความรู้ที่ดี ขอบคุณฉันไม่ได้ตระหนัก
MichaelJones

3
นั่นเป็นคำตอบที่มีประโยชน์สำหรับเหตุผลเฉพาะที่ว่าทำไม OP ต้องการคอลัมน์ของตัวเลขที่เพิ่มเข้าไป แต่สำหรับกรณีที่การเพิ่มหมายเลขทั่วไปมันสั้น (ฉันใช้ "du" ตลอดเวลาเอง แต่ฉันมาที่นี่เพื่อค้นหาคณิตศาสตร์บรรทัดคำสั่ง :-))
Michael H.

12
สิ่งนี้จะไม่ทำงานเมื่อfiles.txtมีขนาดใหญ่ ถ้าจำนวนของการขัดแย้งประปาถึงเกณฑ์บางอย่างก็แบ่งพวกเขาขึ้นในช่วงหลายสายไปxargs duยอดรวมที่แสดงในตอนท้ายคือยอดรวมสำหรับการโทรครั้งสุดท้ายduเท่านั้นไม่ใช่รายการทั้งหมด
Matthew Simoneau



1

นี่คือของฉัน

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

6
+1 สำหรับการพิสูจน์และทุกครั้งที่มีภาษาที่ไม่สวยงามเท่ากว่า Perl :)
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

ฉันชอบที่จะใช้ ....

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

พวกเขาจะแสดงผลรวมของแต่ละบรรทัด ...

ใช้กับสถานการณ์นี้:

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

ผลรวมคือค่าสุดท้าย ...


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

คุณสามารถใช้ awk เพื่อทำเช่นเดียวกันแม้ข้ามจำนวนเต็ม

$ cat files.txt
1
2.3
3.4
ew
1

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

หรือคุณสามารถใช้คำสั่ง ls และคำนวณผลลัพธ์ที่มนุษย์อ่านได้

$ 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

คุณไม่ต้องการแม้แต่ท่อ: awk '{ total += $1} END {print total}' files.txtเร็วกว่า
bmv

0

ในความคิดของฉันทางออกที่ง่ายที่สุดคือคำสั่ง "expr" unix:

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


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

หรือคุณสามารถหาผลรวมได้เมื่อคุณอ่านขนาด

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

หากคุณไม่สนใจขนาดกัดและบล็อกก็โอเค

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

0

หากคุณมี R คุณสามารถใช้:

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

เนื่องจากฉันพอใจกับ R ฉันมีนามแฝงหลายอย่างสำหรับสิ่งนี้ดังนั้นฉันจึงสามารถใช้พวกเขาได้bashโดยไม่ต้องจำไวยากรณ์นี้ ตัวอย่างเช่น

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

ซึ่งให้ฉันทำ

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

แรงบันดาลใจ: มีวิธีรับ min, max, median, และ average ของรายการตัวเลขในคำสั่งเดียวหรือไม่?

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.