Cara portabel untuk mendapatkan ukuran file (dalam byte) di shell?


121

Di Linux, saya menggunakan stat --format="%s" FILE, tetapi Solaris yang saya akses tidak memiliki perintah stat. Lalu apa yang harus saya gunakan?

Saya sedang menulis skrip Bash, dan tidak dapat menginstal perangkat lunak baru pada sistem.

Saya sudah mempertimbangkan untuk menggunakan:

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

atau bahkan:

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

Tapi tak satu pun dari ini terlihat masuk akal - menjalankan Perl hanya untuk mendapatkan ukuran file? Atau menjalankan 2 perintah untuk melakukan hal yang sama?


1
nah skrip bash adalah perangkat lunak, dan jika Anda dapat meletakkannya di sistem, Anda dapat menginstal perangkat lunak.
hanya seseorang

4
Secara teknis - benar. Maksud saya, saya tidak memiliki hak root, dan tidak dapat menginstal paket baru. Tentu menginstal di home dir dimungkinkan. Tetapi tidak juga ketika saya harus membuat script yang portabel, dan instalasi pada mesin "X", paket tambahan baru menjadi rumit.

Jawaban:


207

wc -c < filename(kependekan dari jumlah kata, -cmencetak jumlah byte) adalah solusi portabel, POSIX . Hanya format keluaran yang mungkin tidak seragam di seluruh platform karena beberapa spasi mungkin ditambahkan di awal (yang merupakan kasus untuk Solaris).

Jangan mengabaikan pengalihan input. Ketika file dikirimkan sebagai argumen, nama file dicetak setelah jumlah byte.

Saya khawatir itu tidak akan berfungsi untuk file biner, tetapi berfungsi dengan baik di Linux dan Solaris. Anda bisa mencobanya dengan wc -c < /usr/bin/wc. Selain itu, utilitas POSIX dijamin untuk menangani file biner , kecuali ditentukan lain secara eksplisit.


67
Atau hanya wc -c < filejika Anda tidak ingin nama file muncul.
kafe

34
Jika saya tidak salah, bagaimanapun, wcdalam sebuah pipa harus read()seluruh aliran untuk menghitung byte. The ls/ awksolusi (dan sejenis) menggunakan system call untuk mendapatkan ukuran, yang harus menjadi waktu linear (versus O (ukuran))
jmtd

1
Saya ingat wcmenjadi sangat lambat terakhir kali saya melakukannya pada hard disk penuh. Cukup lambat sehingga saya bisa menulis ulang naskah sebelum yang pertama selesai, datang ke sini untuk mengingat bagaimana saya melakukannya lol.
Camilo Martin

6
Saya tidak akan menggunakan wc -c; terlihat jauh lebih rapi tetapi ls+ awklebih baik untuk kecepatan / penggunaan sumber daya. Juga, saya hanya ingin menunjukkan bahwa Anda sebenarnya perlu melakukan pasca-proses hasil wcjuga karena pada beberapa sistem akan memiliki spasi sebelum hasil, yang mungkin perlu Anda hapus sebelum Anda dapat melakukan perbandingan.
Haravikk

3
wc -cbagus, tetapi tidak akan berfungsi jika Anda tidak memiliki akses baca ke file.
Silas

41

Saya akhirnya menulis program saya sendiri (sangat kecil) untuk menampilkan ukurannya saja. Informasi lebih lanjut di sini: http://fwhacking.blogspot.com/2011/03/bfsize-print-file-size-in-bytes-and.html

Dua cara paling bersih menurut saya dengan alat Linux yang umum adalah:

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

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

Tetapi saya hanya tidak ingin mengetik parameter atau menyalurkan output hanya untuk mendapatkan ukuran file, jadi saya menggunakan bfsize saya sendiri.


2
Baris pertama deskripsi masalah menyatakan bahwa stat bukanlah pilihan, dan wc -c adalah jawaban teratas selama lebih dari setahun sekarang, jadi saya tidak yakin apa gunanya jawaban ini.

22
Intinya adalah orang-orang seperti saya yang menemukan pertanyaan SO ini di Google dan stat merupakan opsi bagi mereka.
yo '22

3
Saya sedang mengerjakan sistem tertanam di mana wc -cmembutuhkan 4090 msec pada file 10 MB vs "0" msec stat -c %s, jadi saya setuju akan sangat membantu untuk memiliki solusi alternatif bahkan ketika mereka tidak menjawab pertanyaan yang diajukan.
Robert Calhoun

3
"stat -c" tidak portabel / tidak menerima argumen yang sama di MacOS seperti di Linux. "wc -c" akan menjadi sangat lambat untuk file besar.
Orwellophile

2
stat juga tidak portabel. stat -c %s /usr/bin/stat stat: illegal option -- c usage: stat [-FlLnqrsx] [-f format] [-t timefmt] [file ...]

27

Meskipun dubiasanya mencetak penggunaan disk dan bukan ukuran data sebenarnya, GNU coreutils dudapat mencetak "ukuran nyata" file dalam byte:

du -b FILE

Tetapi itu tidak akan berfungsi di bawah BSD, Solaris, macOS, ...


3
Pada MacOS X, brew install coreutilsdan gdu -bakan mencapai efek yang sama
Jose Alban

1
Saya lebih suka metode ini karena wcperlu membaca seluruh file sebelum memberikan hasil, dulangsung.
SepupuCocaine

2
POSIX menyebutkan du -bdalam konteks yang sama sekali berbeda dalam dualasannya .
Palec

Ini hanya menggunakan lstatpanggilan, jadi kinerjanya tidak bergantung pada ukuran file. Lebih pendek dari stat -c '%s', tetapi kurang intuitif dan berfungsi secara berbeda untuk folder (mencetak ukuran setiap file di dalamnya).
Palec

FreeBSDdu bisa hampir digunakan du -A -B1, tetapi masih mencetak hasil dalam kelipatan 1024B blok. Tidak berhasil mendapatkannya untuk mencetak jumlah byte. Bahkan pengaturan BLOCKSIZE=1di lingkungan tidak membantu, karena blok 512B digunakan kemudian.
Palec

13

Akhirnya saya memutuskan untuk menggunakan ls, dan bash array expansion:

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

Ini tidak terlalu bagus, tapi setidaknya hanya 1 fork + execve, dan tidak bergantung pada bahasa pemrograman sekunder (perl / ruby ​​/ python / apapun)


Hanya di samping - 'l' dalam '-ln' tidak diperlukan; '-n' persis sama dengan '-ln'
dilarang

Tidak. Bandingkan saja keluarannya.

1
Orang akan menebak ls -ln FILE | { read _ _ _ _ size _ && echo "$size"; }kebutuhan portabel tidak bercabang untuk langkah kedua dari pipeline, karena ia hanya menggunakan built-in, tetapi Bash 4.2.37 pada Linux bercabang dua kali (masih hanya satu execve).
Palec

read _ _ _ _ size _ <<<"$(exec ls -ln /usr/bin/wc)" && echo "$size"bekerja dengan garpu tunggal dan eksekutif tunggal, tetapi menggunakan file sementara untuk string di sini. Ini dapat dibuat portabel dengan mengganti here-string dengan here-document yang sesuai dengan POSX . BTW perhatikan bagian execdalam subkulit. Tanpa itu, Bash melakukan satu garpu untuk subkulit dan satu lagi untuk perintah yang berjalan di dalam. Ini adalah kasus dalam kode yang Anda berikan dalam jawaban ini. terlalu.
Palec

1
Itu -ltidak berguna jika ada -n. Mengutip POSIX lsmanualnya : -n: Hidupkan -l(elo) pilihan, tapi ketika menulis pemilik file atau kelompok, menulis UID numerik file atau GID daripada pengguna atau grup nama, masing-masing. Menonaktifkan -C, -mdan -xpilihan.
Palec

8

Solusi tercepat lintas platform (hanya menggunakan garpu tunggal () untuk ls , tidak mencoba menghitung karakter sebenarnya, tidak memunculkan awk, perl, dll yang tidak diperlukan).

Diuji di MacOS, Linux - mungkin memerlukan sedikit modifikasi untuk Solaris:

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

Jika perlu, sederhanakan argumen ls , dan sesuaikan offset di $ {__ ln [3]}.

Catatan: akan mengikuti symlink.


1
Atau taruh di skrip shell: ls -Lon "$ 1" | awk '{print $ 4}'
Luciano

1
@Luciano Saya pikir Anda benar-benar melewatkan poin untuk tidak bercabang dan melakukan tugas di bash daripada menggunakan bash untuk merangkai banyak perintah unix bersama-sama dengan cara yang tidak efisien.
Orwellophile


6

Saat memproses ls -nkeluaran, sebagai alternatif dari array shell yang tidak portabel, Anda dapat menggunakan argumen posisi, yang merupakan satu-satunya array dan satu-satunya variabel lokal dalam shell standar. Bungkus penimpaan argumen posisi dalam sebuah fungsi untuk mempertahankan argumen asli ke skrip atau fungsi Anda.

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

Ini membagi output ln -dnsesuai dengan IFSpengaturan variabel lingkungan saat ini , menetapkannya ke argumen posisi dan menggemakan yang kelima. The -dMemastikan direktori ditangani dengan benar dan -nmenjamin bahwa nama pengguna dan grup tidak perlu diselesaikan, seperti dengan -l. Selain itu, nama pengguna dan grup yang berisi spasi secara teoritis dapat merusak struktur garis yang diharapkan; mereka biasanya tidak diizinkan, tetapi kemungkinan ini masih membuat programmer berhenti dan berpikir.


5

Jika Anda menggunakan finddari fileutils GNU:

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

Sayangnya, implementasi lain findbiasanya tidak mendukung -maxdepth, atau -printf. Ini adalah kasus untuk Solaris dan macOS find.


FYI maxdepth tidak diperlukan. Ini bisa ditulis ulang sebagai size=$(test -f filename && find filename -printf '%s').
Palec

@Palec: Ini -maxdepthdimaksudkan untuk mencegah findrekursif (karena statOP yang perlu diganti tidak). findPerintah Anda tidak ada -namedan testperintah tidak perlu.
Dijeda sampai pemberitahuan lebih lanjut.

@DennisWilliamson findmencari parameternya secara rekursif untuk file yang cocok dengan kriteria yang diberikan. Jika parameternya bukan direktori, rekurinya adalah… cukup sederhana. Oleh karena itu saya pertama kali menguji itu filenamebenar-benar file biasa yang ada, dan kemudian saya mencetak ukurannya menggunakan findyang tidak dapat digunakan kembali.
Palec

1
find . -maxdepth 1 -type f -name filename -printf '%s'hanya berfungsi jika file ada di direktori saat ini, dan mungkin masih memeriksa setiap file di direktori, yang mungkin lambat. Penggunaan yang lebih baik (bahkan lebih pendek!) find filename -maxdepth 1 -type f -printf '%s'.
Palec

3

Anda dapat menggunakan findperintah untuk mendapatkan beberapa set file (di sini file temp diekstrak). Kemudian Anda dapat menggunakan duperintah untuk mendapatkan ukuran file dari setiap file dalam bentuk yang dapat dibaca manusia menggunakan -hswitch.

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

KELUARAN:

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

Contoh Perl pertama Anda tidak terlihat tidak masuk akal bagi saya.

Karena alasan seperti inilah saya bermigrasi dari menulis skrip shell (di bash / sh dll.) Ke menulis semua kecuali skrip yang paling sepele di Perl. Saya menemukan bahwa saya harus meluncurkan Perl untuk persyaratan tertentu, dan ketika saya melakukannya lebih dan lebih, saya menyadari bahwa menulis skrip di Perl mungkin lebih kuat (dalam hal bahasa dan beragam perpustakaan yang tersedia melalui CPAN ) dan cara yang lebih efisien untuk mencapai apa yang saya inginkan.

Perhatikan bahwa bahasa skrip shell lainnya (misalnya python / ruby) pasti memiliki fasilitas serupa, dan Anda mungkin ingin mengevaluasinya untuk keperluan Anda. Saya hanya membahas Perl karena itulah bahasa yang saya gunakan dan saya kenal.


Ya, saya banyak menulis Perl sendiri, tetapi terkadang alat ini dipilih untuk saya, bukan oleh saya :)

-3

jika Anda memiliki Perl di Solaris Anda, maka gunakanlah. Jika tidak, ls dengan awk adalah pilihan terbaik Anda berikutnya, karena Anda tidak memiliki stat atau temuan Anda bukan GNU find.


-3

Ada trik di Solaris yang pernah saya gunakan, jika Anda meminta ukuran lebih dari satu file, ia hanya mengembalikan ukuran total tanpa nama - jadi sertakan file kosong seperti / dev / null sebagai file kedua:

misalnya file perintah yang Anda inginkan / dev / null

Saya tidak dapat mengingat kembali perintah ukuran mana yang berfungsi untuk ls / wc / etc - sayangnya saya tidak memiliki kotak solaris untuk mengujinya.


-4

di linux Anda dapat menggunakan du -h $FILE, apakah itu juga berfungsi di solaris?


1
Sebenarnya, unit dapat dikonversi, tetapi ini menunjukkan penggunaan disk, bukan ukuran data file ("ukuran yang terlihat").
Palec

-7

Apakah Anda mencoba du -ks | awk '{print $ 1 * 1024}'. Itu mungkin berhasil.


1
Ini menunjukkan penggunaan disk, bukan ukuran data file ("ukuran nyata").
Palec
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.