Cách di động để lấy kích thước tệp (tính bằng byte) trong shell?


121

Trên Linux, tôi sử dụng stat --format="%s" FILE, nhưng Solaris mà tôi có quyền truy cập không có lệnh stat. Tôi nên sử dụng những gì sau đó?

Tôi đang viết tập lệnh Bash và thực sự không thể cài đặt bất kỳ phần mềm mới nào trên hệ thống.

Tôi đã cân nhắc việc sử dụng:

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

hoặc thậm chí:

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

Nhưng cả hai đều không hợp lý - chạy Perl chỉ để lấy kích thước tệp? Hay chạy 2 lệnh để làm giống nhau?


1
bash script phần mềm, và nếu bạn có thể đưa nó vào hệ thống, bạn có thể cài đặt phần mềm.
just somebody

4
Về mặt kỹ thuật - sự thật. Ý tôi là tôi không có đặc quyền root và không thể cài đặt các gói mới. Chắc chắn cài đặt trong nhà dir là có thể. Nhưng không thực sự khi tôi phải tạo tập lệnh có thể di động và cài đặt trên máy "X", các gói bổ sung mới trở nên khó khăn.

Câu trả lời:


207

wc -c < filename(viết tắt của từ đếm, -cin số byte) là một giải pháp POSIX di động . Chỉ có định dạng đầu ra có thể không đồng nhất giữa các nền tảng vì một số khoảng trắng có thể được thêm vào trước (đó là trường hợp của Solaris).

Không bỏ qua chuyển hướng đầu vào. Khi tệp được truyền dưới dạng đối số, tên tệp sẽ được in sau số byte.

Tôi đã lo lắng rằng nó sẽ không hoạt động đối với các tệp nhị phân, nhưng nó hoạt động tốt trên cả Linux và Solaris. Bạn có thể thử nó với wc -c < /usr/bin/wc. Hơn nữa, các tiện ích POSIX được đảm bảo xử lý các tệp nhị phân , trừ khi được chỉ định rõ ràng.


67
Hoặc chỉ wc -c < filekhi bạn không muốn tên tệp xuất hiện.
caf

34
Tuy nhiên, nếu tôi không nhầm, wctrong một đường ống read(), toàn bộ luồng phải đếm các byte. Các ls/ awkgiải pháp (và tương tự) sử dụng lệnh gọi hệ thống để lấy kích thước, phải là thời gian tuyến tính (so với O (kích thước))
jmtd

1
Tôi nhớ lại wcrất chậm trong lần cuối cùng tôi làm điều đó trên một đĩa cứng đầy. Nó đủ chậm để tôi có thể viết lại kịch bản trước khi cái đầu tiên hoàn thành, đến đây để nhớ cách tôi đã làm nó lol.
Camilo Martin

6
Tôi sẽ không sử dụng wc -c; nó trông gọn gàng hơn nhiều nhưng ls+ awktốt hơn cho việc sử dụng tốc độ / tài nguyên. Ngoài ra, tôi chỉ muốn chỉ ra rằng bạn thực sự cần phải xử lý sau kết quả wcbởi vì trên một số hệ thống, nó sẽ có khoảng trắng trước kết quả, bạn có thể cần phải loại bỏ trước khi có thể so sánh.
Haravikk

3
wc -clà tuyệt vời, nhưng nó sẽ không hoạt động nếu bạn không có quyền truy cập đọc vào tệp.
Silas

41

Tôi đã viết một chương trình của riêng mình (thực sự nhỏ) để hiển thị kích thước vừa đủ. Thông tin thêm tại đây: http://fwhacking.blogspot.com/2011/03/bfsize-print-file-size-in-bytes-and.html

Theo ý kiến ​​của tôi, hai cách rõ ràng nhất với các công cụ Linux phổ biến là:

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

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

Nhưng tôi không muốn nhập các thông số hoặc chuyển đầu ra chỉ để có kích thước tệp, vì vậy tôi đang sử dụng bfsize của riêng mình.


2
Dòng đầu tiên của mô tả vấn đề nói rằng stat không phải là một tùy chọn và wc -c là câu trả lời hàng đầu trong hơn một năm nay, vì vậy tôi không chắc chắn điểm của câu trả lời này là gì.

22
Vấn đề là ở những người như tôi, những người tìm thấy câu hỏi SO này trên Google và stat một lựa chọn cho họ.
yo '

3
Tôi đang làm việc trên một hệ thống nhúng trong đó wc -cmất 4090 msec trên tệp 10 MB so với "0" msec stat -c %s, vì vậy tôi đồng ý rằng sẽ hữu ích khi có các giải pháp thay thế ngay cả khi họ không trả lời chính xác câu hỏi được đặt ra.
Robert Calhoun

3
"stat -c" không di động / không chấp nhận các đối số tương tự trên MacOS như trên Linux. "wc -c" sẽ rất chậm đối với các tệp lớn.
Orwellophile,

2
stat cũng không phải là di động. stat -c %s /usr/bin/stat stat: illegal option -- c usage: stat [-FlLnqrsx] [-f format] [-t timefmt] [file ...]

27

Mặc dù duthường in mức sử dụng đĩa chứ không phải kích thước dữ liệu thực tế, các lõi GNU ducó thể in "kích thước biểu kiến" của tệp theo byte:

du -b FILE

Nhưng nó sẽ không hoạt động trong BSD, Solaris, macOS, ...


3
Trên MacOS X, brew install coreutilsgdu -bsẽ đạt được hiệu quả tương tự
Jose Alban

1
Tôi thích phương pháp này hơn vì wccần đọc toàn bộ tệp để đưa ra kết quả dungay lập tức.
CousinCocaine

2
POSIX đề cập du -btrong một bối cảnh hoàn toàn khác về dulý do .
Palec

Điều này chỉ sử dụng lstatcuộc gọi, vì vậy hiệu suất của nó không phụ thuộc vào kích thước tệp. Ngắn hơn stat -c '%s', nhưng kém trực quan hơn và hoạt động khác nhau đối với các thư mục (kích thước bản in của mỗi tệp bên trong).
Palec

FreeBSDdu có thể sử dụng gần hết du -A -B1, nhưng nó vẫn in kết quả dưới dạng bội số của 1024B khối. Không quản lý để có được nó để in số byte. Ngay cả thiết lập BLOCKSIZE=1trong environemnt cũng không giúp ích được gì, vì khối 512B được sử dụng sau đó.
Palec

13

Cuối cùng, tôi quyết định sử dụng ls và mở rộng mảng bash:

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

nó không thực sự đẹp, nhưng ít nhất nó chỉ có 1 fork + execute và nó không dựa vào ngôn ngữ lập trình phụ (perl / ruby ​​/ python / gì)


Chỉ cần một bên - 'l' trong '-ln' là không bắt buộc; '-n' hoàn toàn giống với '-ln'
barryred

Không, không phải. Chỉ cần so sánh kết quả đầu ra.

1
Người ta có thể đoán rằng ls -ln FILE | { read _ _ _ _ size _ && echo "$size"; }nhu cầu di động không cần fork cho bước thứ hai của quy trình, vì nó chỉ sử dụng các bản cài sẵn, nhưng Bash 4.2.37 trên Linux fork hai lần ( execvemặc dù vẫn chỉ là một ).
Palec

read _ _ _ _ size _ <<<"$(exec ls -ln /usr/bin/wc)" && echo "$size"hoạt động với một nhánh và một tệp thực thi duy nhất, nhưng nó sử dụng một tệp tạm thời cho chuỗi đây. Nó có thể được thực hiện di động bằng cách thay thế chuỗi here bằng tài liệu tại đây tuân thủ POSX . BTW lưu ý exectrong vỏ con. Nếu không có điều đó, Bash thực hiện một nhánh rẽ cho vỏ con và một nhánh khác cho lệnh chạy bên trong. Đây là trường hợp trong mã bạn cung cấp trong câu trả lời này. quá.
Palec

1
-lthừa khi có -n. Trích dẫn POSIX lsmanpage : -n: Bật -l(ell) tùy chọn, nhưng khi viết chủ sở hữu hoặc nhóm của tập tin, viết UID số của tập tin hoặc GID chứ không phải là người dùng hoặc nhóm tên tương ứng. Vô hiệu hóa -C, -m-xcác tùy chọn.
Palec

8

Giải pháp nhanh nhất trên nhiều nền tảng (chỉ sử dụng single fork () cho ls , không cố gắng đếm các ký tự thực tế, không sinh ra awk, perl, v.v.) không cần thiết.

Đã thử nghiệm trên MacOS, Linux - có thể yêu cầu sửa đổi nhỏ đối với Solaris:

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

Nếu được yêu cầu, hãy đơn giản hóa các đối số ls và điều chỉnh độ lệch bằng $ {__ ln [3]}.

Lưu ý: sẽ theo sau các liên kết tượng trưng.


1
Hoặc đặt nó trong một tập lệnh shell: ls -Lon "$ 1" | awk '{print $ 4}'
Luciano

1
@Luciano Tôi nghĩ rằng bạn đã hoàn toàn bỏ lỡ quan điểm không phân nhánh và thực hiện một nhiệm vụ trong bash thay vì sử dụng bash để xâu chuỗi nhiều lệnh unix lại với nhau theo cách không hiệu quả.
Orwellophile

8

BSD có statcác tùy chọn khác với lõi GNU, nhưng các khả năng tương tự.

stat -f %z <file name> 

Điều này hoạt động trên macOS (được thử nghiệm trên 10.12), FreeBSD , NetBSDOpenBSD .


Tuy nhiên, Solaris không có stattiện ích gì cả.
Palec

6

Khi xử lý ls -nđầu ra, để thay thế cho mảng shell không di động, bạn có thể sử dụng các đối số vị trí, tạo thành mảng duy nhất và là biến cục bộ duy nhất trong shell tiêu chuẩn. Gói ghi đè các đối số vị trí trong một hàm để giữ nguyên các đối số ban đầu cho tập lệnh hoặc hàm của bạn.

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

Điều này phân chia đầu ra của ln -dntheo IFScài đặt biến môi trường hiện tại , gán nó cho các đối số vị trí và lặp lại đối số thứ năm. Các -dthư mục đảm bảo được xử lý đúng cách và -nđảm bảo rằng tên người dùng và nhóm không cần phải được giải quyết, không giống như với -l. Ngoài ra, tên người dùng và nhóm có chứa khoảng trắng về mặt lý thuyết có thể phá vỡ cấu trúc dòng mong đợi; chúng thường không được phép, nhưng khả năng này vẫn khiến lập trình viên phải dừng lại và suy nghĩ.


5

Nếu bạn sử dụng findtừ tệp GNU:

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

Thật không may, hiện thực khác của findthường không hỗ trợ -maxdepth, cũng không phải -printf. Đây là trường hợp của ví dụ: Solaris và macOS find.


FYI maxdepth là không cần thiết. Nó có thể được viết lại thành size=$(test -f filename && find filename -printf '%s').
Palec

@Palec: Mục -maxdepthđích để ngăn không findbị đệ quy (vì statOP không cần thay thế). findLệnh của bạn thiếu a -nametestlệnh không cần thiết.
Tạm dừng cho đến khi có thông báo mới.

@DennisWilliamson findtìm kiếm các tham số của nó một cách đệ quy để tìm các tệp phù hợp với các tiêu chí đã cho. Nếu các tham số không phải là thư mục, thì việc đệ quy… khá đơn giản. Do đó, đầu tiên tôi kiểm tra xem đó filenamecó thực sự là một tệp thông thường hiện có, và sau đó tôi in kích thước của nó bằng cách sử dụng findmà không có chỗ nào để lặp lại.
Palec

1
find . -maxdepth 1 -type f -name filename -printf '%s'chỉ hoạt động nếu tệp nằm trong thư mục hiện tại và nó vẫn có thể kiểm tra từng tệp trong thư mục, điều này có thể chậm. Sử dụng tốt hơn (thậm chí ngắn hơn!) find filename -maxdepth 1 -type f -printf '%s'.
Palec

3

Bạn có thể sử dụng findlệnh để lấy một số tập hợp tệp (ở đây tệp tạm thời được giải nén). Sau đó, bạn có thể sử dụng dulệnh để lấy kích thước tệp của mỗi tệp ở dạng con người có thể đọc được bằng cách sử dụng -hchuyển đổi.

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

ĐẦU RA:

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

Ví dụ Perl đầu tiên của bạn có vẻ không hợp lý với tôi.

Vì những lý do như thế này mà tôi đã chuyển từ việc viết các tập lệnh shell (trong bash / sh, v.v.) sang viết tất cả trừ các tập lệnh tầm thường nhất trong Perl. Tôi nhận thấy rằng tôi đang phải khởi chạy Perl cho các yêu cầu cụ thể và khi tôi làm điều đó ngày càng nhiều, tôi nhận ra rằng việc viết các tập lệnh trong Perl có lẽ mạnh hơn (về ngôn ngữ và một loạt các thư viện có sẵn thông qua CPAN ) và cách hiệu quả hơn để đạt được những gì tôi muốn.

Lưu ý rằng các ngôn ngữ shell-scripting khác (ví dụ như python / ruby) chắc chắn sẽ có các cơ sở tương tự và bạn có thể muốn đánh giá chúng cho mục đích của mình. Tôi chỉ thảo luận về Perl vì đó là ngôn ngữ tôi sử dụng và quen thuộc.


Vâng, tôi làm rất nhiều Perl viết bản thân mình, nhưng đôi khi công cụ được lựa chọn đối với tôi, không phải của tôi :)

-3

nếu bạn có Perl trên Solaris của mình, thì hãy sử dụng nó. Nếu không, ls với awk là đặt cược tốt nhất tiếp theo của bạn, vì bạn không có thống kê hoặc tìm thấy của bạn không phải là tìm thấy GNU.


-3

Có một mẹo trong Solaris mà tôi đã sử dụng, nếu bạn yêu cầu kích thước của nhiều hơn một tệp, nó chỉ trả về tổng kích thước mà không có tên - vì vậy hãy bao gồm một tệp trống như / dev / null làm tệp thứ hai:

ví dụ: lệnh fileyouwant / dev / null

Tôi không thể nhớ lệnh kích thước nào hoạt động cho ls / wc / etc - tiếc là tôi không có hộp solaris để kiểm tra nó.


-4

trên linux bạn có thể sử dụng du -h $FILE, điều đó cũng hoạt động trên solaris?


1
Trên thực tế, các đơn vị có thể được chuyển đổi, nhưng điều này cho thấy mức sử dụng đĩa thay vì kích thước dữ liệu tệp ("kích thước biểu kiến").
Palec

-7

Bạn đã thử du -ks | awk '{in $ 1 * 1024}'. Điều đó có thể chỉ hoạt động.


1
Điều này hiển thị mức sử dụng đĩa thay vì kích thước dữ liệu tệp ("kích thước biểu kiến").
Palec
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.