วิธีพกพาในการรับขนาดไฟล์ (เป็นไบต์) ในเชลล์?


121

บน Linux ฉันใช้stat --format="%s" FILEแต่ Solaris ที่ฉันเข้าถึงไม่มีคำสั่ง stat ฉันควรใช้อะไร?

ฉันกำลังเขียนสคริปต์ Bash และไม่สามารถติดตั้งซอฟต์แวร์ใหม่ในระบบได้

ฉันพิจารณาแล้วว่าใช้:

perl -e '@x=stat(shift);print $x[7]' FILE

หรือแม้กระทั่ง:

ls -nl FILE | awk '{print $5}'

แต่สิ่งเหล่านี้ดูไม่สมเหตุสมผล - ใช้ Perl เพื่อรับขนาดไฟล์? หรือรัน 2 คำสั่งทำเหมือนกัน?


1
สคริปต์ทุบตีเป็นซอฟต์แวร์และหากคุณสามารถใส่มันลงในระบบคุณสามารถติดตั้งซอฟต์แวร์ได้
แค่ใครสักคน

4
ในทางเทคนิค - จริง ฉันหมายความว่าฉันไม่มีสิทธิ์รูทและไม่สามารถติดตั้งแพ็คเกจใหม่ได้ แน่นอนว่าสามารถติดตั้งใน dir บ้านได้ แต่เมื่อฉันต้องสร้างสคริปต์ที่พกพาได้และการติดตั้งบนเครื่อง "X" แพ็กเกจเพิ่มเติมใหม่จะยุ่งยาก

คำตอบ:


207

wc -c < filename(ย่อมาจาก word count -cพิมพ์จำนวนไบต์) เป็นโซลูชันPOSIXแบบพกพา เฉพาะรูปแบบเอาต์พุตเท่านั้นที่อาจไม่เหมือนกันในทุกแพลตฟอร์มเนื่องจากช่องว่างบางส่วนอาจถูกนำหน้า (ซึ่งเป็นกรณีของ Solaris)

อย่าละเว้นการเปลี่ยนทิศทางอินพุต เมื่อไฟล์ถูกส่งเป็นอาร์กิวเมนต์ชื่อไฟล์จะถูกพิมพ์หลังจากจำนวนไบต์

ฉันกังวลว่ามันจะใช้ไม่ได้กับไฟล์ไบนารี แต่ทำงานได้ดีทั้งบน Linux และ Solaris คุณสามารถลองใช้กับwc -c < /usr/bin/wc. ยิ่งไปกว่านั้นยูทิลิตี้ POSIX ยังรับประกันว่าจะจัดการไฟล์ไบนารีเว้นแต่จะระบุไว้อย่างชัดเจน


67
หรือwc -c < fileหากคุณไม่ต้องการให้ชื่อไฟล์ปรากฏ
คาเฟ่

34
ถ้าฉันไม่เข้าใจผิดwcในไปป์ไลน์ต้องread()สตรีมทั้งหมดเพื่อนับไบต์ ls/ awkการแก้ปัญหา (และคล้ายกัน) ใช้เรียกระบบเพื่อให้ได้ขนาดที่ควรจะเป็นเส้นเวลา (เทียบกับ O (ขนาด))
jmtd

1
ฉันจำได้wcว่ามันช้ามากในครั้งสุดท้ายที่ทำแบบนั้นบนฮาร์ดดิสก์แบบเต็ม มันช้ามากพอที่ฉันจะเขียนบทซ้ำได้ก่อนที่บทแรกจะเสร็จมาที่นี่เพื่อจำว่าฉันทำมันได้อย่างไรฮ่า ๆ
Camilo Martin

6
ฉันจะไม่ใช้wc -c; มันดูดีกว่ามาก แต่ls+ awkดีกว่าสำหรับความเร็ว / การใช้ทรัพยากร นอกจากนี้ฉันแค่อยากจะชี้ให้เห็นว่าคุณจำเป็นต้องโพสต์ผลลัพธ์ของผลลัพธ์ด้วยwcเช่นกันเพราะในบางระบบจะมีช่องว่างก่อนผลลัพธ์ซึ่งคุณอาจต้องตัดออกก่อนจึงจะทำการเปรียบเทียบได้
Haravikk

3
wc -cดีมาก แต่จะใช้งานไม่ได้หากคุณไม่มีสิทธิ์อ่านไฟล์
สิลา

41

จบลงด้วยการเขียนโปรแกรมของตัวเอง (เล็กมาก) เพื่อแสดงขนาด ข้อมูลเพิ่มเติมที่นี่: http://fwhacking.blogspot.com/2011/03/bfsize-print-file-size-in-bytes-and.html

สองวิธีที่สะอาดที่สุดในความคิดของฉันกับเครื่องมือ Linux ทั่วไปคือ:

$ stat -c %s /usr/bin/stat
50000

$ wc -c < /usr/bin/wc
36912

แต่ฉันไม่ต้องการพิมพ์พารามิเตอร์หรือไพพ์เอาต์พุตเพื่อให้ได้ขนาดไฟล์ดังนั้นฉันจึงใช้ bfsize ของตัวเอง


2
บรรทัดแรกของคำอธิบายปัญหาระบุว่า stat ไม่ใช่ตัวเลือกและ wc -c เป็นคำตอบยอดนิยมมานานกว่าหนึ่งปีแล้วดังนั้นฉันไม่แน่ใจว่าอะไรคือประเด็นของคำตอบนี้

22
ประเด็นอยู่ที่คนอย่างฉันที่พบคำถาม SO นี้ใน Google และstat เป็นตัวเลือกสำหรับพวกเขา
โย '22

3
ฉันกำลังทำงานกับระบบฝังตัวที่wc -cใช้เวลา 4090 msec ในไฟล์ 10 MB เทียบกับ "0" msec สำหรับstat -c %sดังนั้นฉันจึงเห็นด้วยว่าการมีทางเลือกอื่นเป็นประโยชน์แม้ว่าจะไม่ได้ตอบคำถามที่ตรงประเด็นก็ตาม
Robert Calhoun

3
"stat -c" ไม่สามารถพกพาได้ / ไม่ยอมรับอาร์กิวเมนต์เดียวกันบน MacOS เช่นเดียวกับบน Linux "wc -c" จะช้ามากสำหรับไฟล์ขนาดใหญ่
Orwellophile

2
stat ไม่สามารถพกพาได้เช่นกัน stat -c %s /usr/bin/stat stat: illegal option -- c usage: stat [-FlLnqrsx] [-f format] [-t timefmt] [file ...]

27

แม้ว่าduโดยปกติจะพิมพ์การใช้งานดิสก์และไม่ใช่ขนาดข้อมูลจริง GNU coreutils duสามารถพิมพ์ "ขนาดที่ชัดเจน" ของไฟล์เป็นไบต์:

du -b FILE

แต่จะไม่ทำงานภายใต้ BSD, Solaris, macOS, ...


3
บน MacOS X brew install coreutilsและgdu -bจะได้ผลเช่นเดียวกัน
Jose Alban

1
ฉันชอบวิธีนี้เพราะwcต้องอ่านทั้งไฟล์ก่อนเพื่อให้ผลลัพธ์duเป็นทันที
CousinCocaine

2
POSIX กล่าวถึงdu -bในบริบทที่แตกต่างกันอย่างสมบูรณ์ในเหตุผลdu
Palec

ใช้เพียงการlstatโทรดังนั้นประสิทธิภาพจึงไม่ขึ้นอยู่กับขนาดไฟล์ สั้นกว่าstat -c '%s'แต่ใช้งานง่ายน้อยกว่าและทำงานต่างกันสำหรับโฟลเดอร์ (ขนาดพิมพ์ของแต่ละไฟล์ภายใน)
Palec

FreeBSDduสามารถใช้งานได้อย่างใกล้ชิดdu -A -B1แต่ยังคงพิมพ์ผลลัพธ์เป็นบล็อก 1024B หลายรายการ ไม่ได้จัดการเพื่อให้พิมพ์จำนวนไบต์ แม้แต่การตั้งค่าBLOCKSIZE=1ในสภาพแวดล้อมก็ไม่ช่วยเพราะใช้บล็อก 512B แล้ว
Palec

13

ในที่สุดฉันก็ตัดสินใจใช้ ls และ bash array expansion:

TEMP=( $( ls -ln FILE ) )
SIZE=${TEMP[4]}

มันไม่ดีจริงๆ แต่อย่างน้อยมันก็ทำเพียง 1 fork + execve และไม่ต้องพึ่งพาภาษาโปรแกรมรอง (perl / ruby ​​/ python / อะไรก็ตาม)


เพียงแค่กัน - ไม่จำเป็นต้องใช้ 'l' in '-ln'; '-n' เหมือนกับ '-ln' ทุก
ประการ

ไม่มันไม่ใช่. เพียงเปรียบเทียบผลลัพธ์

1
ใครจะเดาว่าอุปกรณ์พกพาls -ln FILE | { read _ _ _ _ size _ && echo "$size"; }ไม่จำเป็นต้องแยกสำหรับขั้นตอนที่สองของไปป์ไลน์เนื่องจากใช้เพียงแค่ในตัว แต่ Bash 4.2.37 บน Linux forks สองครั้ง (ยังคงเป็นเพียงอันเดียวexecve)
Palec

read _ _ _ _ size _ <<<"$(exec ls -ln /usr/bin/wc)" && echo "$size"ใช้งานได้กับ single fork และ single exec แต่ใช้ไฟล์ชั่วคราวสำหรับสตริงที่นี่ มันสามารถทำแบบพกพาโดยการเปลี่ยนที่นี่สตริงกับ POSX ที่สอดคล้องกับที่นี่เอกสาร BTW จดบันทึกexecใน subshell หากไม่มีสิ่งนั้น Bash จะทำการแยกหนึ่งตัวสำหรับ subshell และอีกอันสำหรับคำสั่งที่ทำงานอยู่ภายใน นี่เป็นกรณีในรหัสที่คุณระบุในคำตอบนี้ เกินไป.
Palec

1
เป็นฟุ่มเฟือยในการปรากฏตัวของ-l -nการอ้างถึงPOSIX lsmanpage :: -nเปิด-lตัวเลือก (ell) แต่เมื่อเขียนเจ้าของหรือกลุ่มของไฟล์ให้เขียน UID หรือ GID ที่เป็นตัวเลขของไฟล์แทนชื่อผู้ใช้หรือกลุ่มตามลำดับ ปิดการใช้งาน-C, -mและ-xตัวเลือก
Palec

8

โซลูชันที่เร็วที่สุดข้ามแพลตฟอร์ม (ใช้เฉพาะส้อมเดียว () สำหรับlsไม่พยายามนับอักขระจริงไม่วางไข่ awk perl และอื่น ๆ ที่ไม่จำเป็น)

ทดสอบบน MacOS, Linux - อาจต้องมีการปรับเปลี่ยนเล็กน้อยสำหรับ Solaris:

__ln=( $( ls -Lon "$1" ) )
__size=${__ln[3]}
echo "Size is: $__size bytes"

หากจำเป็นให้ลดความซับซ้อนของอาร์กิวเมนต์lsและปรับค่าชดเชยใน $ {__ ln [3]}

หมายเหตุ: จะเป็นไปตาม symlinks


1
หรือใส่ไว้ในเชลล์สคริปต์: ls -Lon "$ 1" | awk '{print $ 4}'
Luciano

1
@Luciano ฉันคิดว่าคุณพลาดจุดที่จะไม่ฟอร์คและทำภารกิจในการทุบตีแทนที่จะใช้ bash เพื่อรวมคำสั่งยูนิกซ์จำนวนมากเข้าด้วยกันในแบบที่ไม่มีประสิทธิภาพ
Orwellophile

8

BSD มีstatตัวเลือกที่แตกต่างจาก GNU coreutils one แต่มีความสามารถใกล้เคียงกัน

stat -f %z <file name> 

งานเกี่ยวกับเรื่องนี้MacOS (การทดสอบเกี่ยวกับ 10.12), FreeBSD , NetBSDและOpenBSD


Solaris ไม่มีstatยูทิลิตี้เลย
Palec

6

เมื่อประมวลผลls -nเอาต์พุตเป็นอีกทางเลือกหนึ่งของเชลล์อาร์เรย์ที่ไม่สามารถพกพาได้คุณสามารถใช้อาร์กิวเมนต์ตำแหน่งซึ่งสร้างอาร์เรย์เดียวและเป็นตัวแปรเฉพาะที่ในเชลล์มาตรฐาน ตัดการเขียนทับของอาร์กิวเมนต์ตำแหน่งในฟังก์ชันเพื่อรักษาอาร์กิวเมนต์ดั้งเดิมของสคริปต์หรือฟังก์ชันของคุณ

getsize() { set -- $(ls -dn "$1") && echo $5; }
getsize FILE

สิ่งนี้จะแยกเอาต์พุตของln -dnตามการIFSตั้งค่าตัวแปรสภาพแวดล้อมปัจจุบันกำหนดให้กับอาร์กิวเมนต์ตำแหน่งและสะท้อนค่าที่ห้า -dไดเรกทอรีมั่นใจได้รับการจัดการอย่างถูกต้องและมั่นใจว่าชื่อผู้ใช้และกลุ่มไม่จำเป็นต้องได้รับการแก้ไขแตกต่างกับ-n -lนอกจากนี้ชื่อผู้ใช้และกลุ่มที่มีช่องว่างอาจทำลายโครงสร้างบรรทัดที่คาดไว้ในทางทฤษฎี พวกเขามักจะไม่ได้รับอนุญาต แต่ความเป็นไปได้นี้ยังทำให้โปรแกรมเมอร์หยุดและคิด


5

หากคุณใช้findจากไฟล์ GNU:

size=$( find . -maxdepth 1 -type f -name filename -printf '%s' )

แต่น่าเสียดายที่การใช้งานอื่น ๆ ของfindมักจะไม่ได้สนับสนุนหรือ-maxdepth -printfเป็นกรณีนี้สำหรับเช่น Solaris และ findMacOS


ไม่จำเป็นต้องใช้ FYI maxdepth สามารถเขียนใหม่เป็นsize=$(test -f filename && find filename -printf '%s')ไฟล์.
Palec

@Palec: -maxdepthมีวัตถุประสงค์เพื่อป้องกันไม่ให้เกิดfindซ้ำ (เนื่องจากไม่statจำเป็นต้องเปลี่ยน OP) findคำสั่งของคุณไม่มี a -nameและtestคำสั่งนั้นไม่จำเป็น
หยุดชั่วคราวจนกว่าจะมีประกาศอีกครั้ง

@DennisWilliamson findค้นหาพารามิเตอร์ซ้ำสำหรับไฟล์ที่ตรงกับเกณฑ์ที่กำหนด หากพารามิเตอร์ไม่ใช่ไดเร็กทอรีการเรียกซ้ำจะ ... ค่อนข้างง่าย ดังนั้นฉันจึงทดสอบก่อนว่าfilenameเป็นไฟล์ธรรมดาที่มีอยู่แล้วจากนั้นฉันก็พิมพ์ขนาดของมันโดยใช้findที่ไม่มีที่ไหนให้เรียกคืน
Palec

1
find . -maxdepth 1 -type f -name filename -printf '%s'ทำงานก็ต่อเมื่อไฟล์อยู่ในไดเร็กทอรีปัจจุบันและอาจยังตรวจสอบไฟล์แต่ละไฟล์ในไดเร็กทอรีซึ่งอาจทำงานช้า การใช้งานที่ดีขึ้น find filename -maxdepth 1 -type f -printf '%s'(แม้สั้น!)
Palec

3

คุณสามารถใช้findคำสั่งเพื่อรับชุดไฟล์ (ที่นี่ไฟล์ temp จะถูกแยกออก) จากนั้นคุณสามารถใช้duคำสั่งเพื่อรับขนาดไฟล์ของแต่ละไฟล์ในรูปแบบที่มนุษย์อ่านได้โดยใช้-hสวิตช์

find $HOME -type f -name "*~" -exec du -h {} \;

เอาท์พุท:

4.0K    /home/turing/Desktop/JavaExmp/TwoButtons.java~
4.0K    /home/turing/Desktop/JavaExmp/MyDrawPanel.java~
4.0K    /home/turing/Desktop/JavaExmp/Instream.java~
4.0K    /home/turing/Desktop/JavaExmp/RandomDemo.java~
4.0K    /home/turing/Desktop/JavaExmp/Buff.java~
4.0K    /home/turing/Desktop/JavaExmp/SimpleGui2.java~

2

ตัวอย่างแรกของคุณ Perl ดูไม่สมเหตุสมผลสำหรับฉัน

ด้วยเหตุผลเช่นนี้ฉันจึงย้ายจากการเขียนเชลล์สคริปต์ (ใน bash / sh เป็นต้น) ไปเขียนสคริปต์ที่ไม่สำคัญที่สุดใน Perl ฉันพบว่าฉันต้องเปิดใช้ Perl สำหรับข้อกำหนดเฉพาะและเมื่อฉันทำเช่นนั้นมากขึ้นเรื่อย ๆ ฉันก็ตระหนักว่าการเขียนสคริปต์ใน Perl นั้นน่าจะมีประสิทธิภาพมากกว่า (ในแง่ของภาษาและไลบรารีที่หลากหลายที่มีให้ผ่านCPAN ) และวิธีที่มีประสิทธิภาพมากขึ้นในการบรรลุสิ่งที่ฉันต้องการ

โปรดทราบว่าภาษาเชลล์สคริปต์อื่น ๆ (เช่น python / ruby) จะมีสิ่งอำนวยความสะดวกที่คล้ายคลึงกันอย่างไม่ต้องสงสัยและคุณอาจต้องการประเมินสิ่งเหล่านี้ตามวัตถุประสงค์ของคุณ ฉันพูดถึง Perl เท่านั้นเนื่องจากเป็นภาษาที่ฉันใช้และคุ้นเคย


ฉันเขียน Perl หลายอย่างด้วยตัวเอง แต่บางครั้งเครื่องมือก็ถูกเลือกให้ฉันไม่ใช่ฉัน :)

-3

หากคุณมี Perl บน Solaris ของคุณให้ใช้มัน มิฉะนั้น ls with awk จะเป็นทางออกที่ดีที่สุดต่อไปของคุณเนื่องจากคุณไม่มี stat หรือการค้นหาของคุณไม่พบ GNU


-3

มีเคล็ดลับใน Solaris ที่ฉันใช้ถ้าคุณขอขนาดของไฟล์มากกว่าหนึ่งไฟล์มันจะส่งคืนเพียงขนาดทั้งหมดโดยไม่มีชื่อดังนั้นให้รวมไฟล์ว่างเช่น / dev / null เป็นไฟล์ที่สอง:

เช่นไฟล์คำสั่งคุณต้องการ / dev / null

ฉันจำไม่ได้ว่าคำสั่งขนาดใดที่ใช้ได้กับ ls / wc / etc - น่าเสียดายที่ฉันไม่มีกล่อง solaris ให้ทดสอบ


-4

บนลินุกซ์ที่คุณสามารถใช้ได้du -h $FILEนั้นใช้ได้กับโซลาริสด้วยหรือไม่?


1
ที่จริงแล้วสามารถแปลงหน่วยได้ แต่จะแสดงการใช้ดิสก์แทนขนาดข้อมูลไฟล์ ("ขนาดที่ชัดเจน")
Palec

-7

คุณลอง du -ks | awk "{พิมพ์ $ 1 * 1024}" นั่นอาจใช้ได้ผล


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