Przenośny sposób na uzyskanie rozmiaru pliku (w bajtach) w powłoce?

121

Na Linuksie używam stat --format="%s" FILE, ale Solaris, do którego mam dostęp, nie ma polecenia stat. Czego więc powinienem użyć?

Piszę skrypty Bash i tak naprawdę nie mogę zainstalować żadnego nowego oprogramowania w systemie.

Rozważałem już użycie:

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

lub nawet:

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

Ale żadne z tych nie wygląda rozsądnie - uruchomienie Perla tylko po to, aby uzyskać rozmiar pliku? Lub uruchamiasz 2 polecenia, aby zrobić to samo?


źródło
1
cóż, skrypt bash to oprogramowanie i jeśli umieścisz to w systemie, możesz zainstalować oprogramowanie.
tylko ktoś
4
Technicznie - prawda. Chodziło mi o to, że nie mam uprawnień roota i nie mogę instalować nowych pakietów. Na pewno instalacja w katalogu domowym jest możliwa. Ale nie do końca, kiedy muszę zrobić skrypt, który jest przenośny i zainstalować go na maszynach "X", nowe dodatkowe pakiety stają się trudne.

Odpowiedzi:

207

wc -c < filename(skrót od liczby słów, -cwyświetla liczbę bajtów) jest przenośnym rozwiązaniem POSIX . Tylko format wyjściowy może nie być jednolity na różnych platformach, ponieważ niektóre spacje mogą być dołączane na początku (tak jest w przypadku Solaris).

Nie pomijaj przekierowania danych wejściowych. Gdy plik jest przekazywany jako argument, nazwa pliku jest wypisywana po liczbie bajtów.

Martwiłem się, że to nie zadziała dla plików binarnych, ale działa dobrze zarówno na Linuksie, jak i na Solarisie. Możesz to wypróbować wc -c < /usr/bin/wc. Co więcej, narzędzia POSIX gwarantują obsługę plików binarnych , chyba że wyraźnie określono inaczej.

Carl Smotricz
źródło
67
Lub po prostu wc -c < filejeśli nie chcesz, aby nazwa pliku się pojawiała.
kawiarnia
34
Jeśli się nie mylę, wcw potoku musi read()cały strumień policzyć bajty. Rozwiązania ls/ awk(i podobne) używają wywołania systemowego, aby uzyskać rozmiar, który powinien być czasem liniowym (w porównaniu do O (rozmiar))
jmtd
1
Pamiętam, wcże byłem bardzo powolny, kiedy ostatnio robiłem to na pełnym dysku twardym. Był na tyle wolny, że mogłem ponownie napisać scenariusz, zanim skończyłem pierwszy, przyszedłem tutaj, aby przypomnieć sobie, jak to zrobiłem lol.
Camilo Martin
6
Nie użyłbym wc -c; wygląda o wiele schludniej, ale ls+ awkjest lepsze pod względem szybkości / wykorzystania zasobów. Chciałem też tylko zwrócić uwagę, że w rzeczywistości musisz przetworzyć wyniki wcrównież w programie, ponieważ w niektórych systemach będzie on zawierał spacje przed wynikiem, które możesz potrzebować usunąć przed wykonaniem porównań.
Haravikk
3
wc -cjest świetny, ale nie zadziała, jeśli nie masz uprawnień do odczytu pliku.
Silas
41

Skończyło się na napisaniu własnego programu (naprawdę małego), który wyświetlał tylko rozmiar. Więcej informacji tutaj: http://fwhacking.blogspot.com/2011/03/bfsize-print-file-size-in-bytes-and.html

Moim zdaniem dwa najbardziej czyste sposoby korzystania z typowych narzędzi Linuksa to:

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

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

Ale ja po prostu nie chcę wpisywać parametrów ani potokować wyjścia tylko po to, aby uzyskać rozmiar pliku, więc używam własnego rozmiaru bfsize.

fwhacking
źródło
2
Pierwsza linia opisu problemu stwierdza, że ​​stat nie jest opcją, a wc -c jest najlepszą odpowiedzią od ponad roku, więc nie jestem pewien, jaki jest sens tej odpowiedzi.
22
Chodzi o ludzi takich jak ja, którzy znajdują to pytanie SO w Google i stat jest dla nich opcją.
yo '22
3
Pracuję na systemie wbudowanym, w którym wc -cplik 10 MB zajmuje 4090 ms w porównaniu z „0” ms stat -c %s, więc zgadzam się, że warto mieć alternatywne rozwiązania, nawet jeśli nie odpowiadają one dokładnie na zadane pytanie.
Robert Calhoun
3
„stat -c” nie jest przenośne / nie akceptuje tych samych argumentów w systemie MacOS, co w systemie Linux. "wc -c" będzie bardzo powolne dla dużych plików.
Orwellophile
2
stat również nie jest przenośny. stat -c %s /usr/bin/stat stat: illegal option -- c usage: stat [-FlLnqrsx] [-f format] [-t timefmt] [file ...]
27

Mimo że duzwykle wyświetla użycie dysku, a nie rzeczywisty rozmiar danych, GNU coreutils dumoże wypisać „pozorny rozmiar” pliku w bajtach:

du -b FILE

Ale to nie będzie działać pod BSD, Solaris, macOS, ...

fwhacking
źródło
3
Na MacOS X brew install coreutilsi gdu -bosiągnie ten sam efekt
Jose Alban
1
Wolę tę metodę, ponieważ wctrzeba przeczytać cały plik, zanim da wynik, dujest natychmiastowy.
CousinCocaine
2
POSIX wspomina du -bw zupełnie innym kontekście w duuzasadnieniu .
Palec
Używa tylko lstatwywołania, więc jego wydajność nie zależy od rozmiaru pliku. Krótszy niż stat -c '%s', ale mniej intuicyjny i działa inaczej dla folderów (drukuje rozmiar każdego pliku w środku).
Palec
FreeBSDdu może zbliżyć się za pomocą du -A -B1, ale nadal wyświetla wynik jako wielokrotność bloków 1024B. Nie udało się go zmusić do wydrukowania liczby bajtów. Nawet ustawienie BLOCKSIZE=1w środowisku nie pomaga, ponieważ używa się wtedy bloku 512B.
Palec
13

W końcu zdecydowałem się użyć ls i rozszerzenia tablicy bash:

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

to nie jest zbyt ładne, ale przynajmniej robi tylko 1 fork + execve i nie opiera się na dodatkowym języku programowania (perl / ruby ​​/ python / cokolwiek)


źródło
Na marginesie - „l” w „-ln” nie jest wymagane; `` -n '' jest dokładnie tym samym, co `` -ln ''
zablokowane
Nie, nie jest. Po prostu porównaj wyniki.
1
Można by przypuszczać, że przenośny ls -ln FILE | { read _ _ _ _ size _ && echo "$size"; }nie potrzebuje rozwidlać w drugim etapie potoku, ponieważ używa tylko wbudowanych, ale Bash 4.2.37 na forkach Linuksa dwukrotnie ( execvechoć wciąż tylko jeden ).
Palec
read _ _ _ _ size _ <<<"$(exec ls -ln /usr/bin/wc)" && echo "$size"działa z pojedynczym rozwidleniem i pojedynczym wykonaniem, ale używa pliku tymczasowego jako ciągu tutaj. Można go uczynić przenośnym, zastępując ciąg znaków tutaj dokumentem tutaj zgodnym z POSX . BTW zwróć uwagę execna podpowłokę. Bez tego Bash wykonuje jeden fork dla podpowłoki i drugi dla polecenia działającego wewnątrz. Tak jest w przypadku kodu, który podajesz w tej odpowiedzi. też.
Palec
1
-lJest zbędny w obecności -n. Cytowanie POSIX lsmanpage : -n: Włącz -l(ell) opcja, ale pisząc właściciela pliku lub grupy, pisać UID numeryczna pliku lub GID zamiast nazwy użytkownika lub grupy, odpowiednio. Wyłącz -C, -moraz -xopcje.
Palec
8

Najszybsze rozwiązanie dla wielu platform (używa tylko pojedynczego fork () dla ls , nie próbuje zliczać rzeczywistych znaków, nie tworzy niepotrzebnych awk, perl itp.).

Testowane na MacOS, Linux - może wymagać niewielkich modyfikacji dla Solaris:

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

W razie potrzeby uprość argumenty ls i dostosuj przesunięcie w $ {__ ln [3]}.

Uwaga: nastąpi podążanie za dowiązaniami symbolicznymi.

Orwellophile
źródło
1
Lub umieść go w skrypcie powłoki: ls -Lon "$ 1" | awk '{print $ 4}'
Luciano
1
@Luciano Myślę, że całkowicie przegapiłeś sens nie rozwidlania i wykonywania zadań w bashu, zamiast używania basha do łączenia wielu poleceń unixa w nieefektywny sposób.
Orwellophile
8

BSD mają statinne opcje niż te z GNU coreutils, ale podobne możliwości.

stat -f %z <file name> 

Działa to na macOS (testowane 10.12), FreeBSD , NetBSD i OpenBSD .

user7504315
źródło
Solaris nie ma jednak żadnej statużyteczności.
Palec
6

Podczas przetwarzania danych ls -nwyjściowych, jako alternatywa dla źle przenośnych tablic powłoki, można użyć argumentów pozycyjnych, które tworzą jedyną tablicę i są jedynymi zmiennymi lokalnymi w standardowej powłoce. Zawiń nadpisanie argumentów pozycyjnych w funkcji, aby zachować oryginalne argumenty w swoim skrypcie lub funkcji.

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

Spowoduje to podzielenie wyniku programu ln -dnzgodnie z bieżącymi IFSustawieniami zmiennych środowiskowych, przypisanie go do argumentów pozycyjnych i powtórzenie piątego. W -dgwarantuje katalogi są właściwie traktowane i -nzapewnia, że nazwy użytkowników i grupy nie muszą być rozwiązane, w przeciwieństwie do -l. Ponadto nazwy użytkowników i grup zawierające białe znaki mogą teoretycznie złamać oczekiwaną strukturę linii; są one zwykle niedozwolone, ale taka możliwość wciąż sprawia, że ​​programista zatrzymuje się i myśli.

Richard
źródło
5

Jeśli używasz findplików z GNU fileutils:

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

Niestety, inne implementacje findzwykle nie obsługują -maxdepth, ani -printf. Tak jest w przypadku np. Solaris i macOS find.

Wstrzymano do odwołania.
źródło
FYI maxdepth nie jest potrzebne. Można go przepisać jako size=$(test -f filename && find filename -printf '%s').
Palec
@Palec: -maxdepthma na celu zapobieganie findrekurencyjności (ponieważ operacja, statktóra musi zostać zastąpiona, nie jest). W Twoim findpoleceniu brakuje a, -namea testpolecenie nie jest konieczne.
Wstrzymano do odwołania.
@DennisWilliamson findprzeszukuje swoje parametry rekurencyjnie w poszukiwaniu plików spełniających podane kryteria. Jeśli parametry nie są katalogami, rekurencja jest… dość prosta. Dlatego najpierw testuję, filenameczy naprawdę jest to zwykły plik, a następnie drukuję jego rozmiar, używając findtego, który nie ma gdzie się powtarzać.
Palec
1
find . -maxdepth 1 -type f -name filename -printf '%s'działa tylko wtedy, gdy plik znajduje się w bieżącym katalogu i może nadal badać każdy plik w katalogu, co może być powolne. Lepsze wykorzystanie (jeszcze krótsze!) find filename -maxdepth 1 -type f -printf '%s'.
Palec
3

Możesz użyć findpolecenia, aby pobrać zestaw plików (tutaj pliki tymczasowe są rozpakowywane). Następnie możesz użyć dupolecenia, aby uzyskać rozmiar każdego pliku w postaci czytelnej dla człowieka za pomocą -hprzełącznika.

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

WYNIK:

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~
Abhishek Singh
źródło
2

Twój pierwszy przykład w Perlu nie wydaje mi się nierozsądny.

Z takich powodów przeszedłem od pisania skryptów powłoki (w bash / sh itp.) Do pisania wszystkich, oprócz najbardziej trywialnych skryptów w Perlu. Odkryłem, że muszę uruchamiać Perla dla określonych wymagań, a gdy robiłem to coraz częściej, zdałem sobie sprawę, że pisanie skryptów w Perlu jest prawdopodobnie bardziej wydajne (pod względem języka i szerokiej gamy bibliotek dostępnych przez CPAN ) i skuteczniejszy sposób na osiągnięcie tego, co chciałem.

Zauważ, że inne języki skryptowe powłoki (np. Python / ruby) bez wątpienia będą miały podobne udogodnienia i możesz chcieć je wypróbować do swoich celów. Omawiam Perla tylko dlatego, że jest to język, którego używam i który znam.

Brian Agnew
źródło
Cóż, sam dużo piszę w Perlu, ale czasami narzędzie jest wybierane przeze mnie, a nie przeze mnie :)
-3

jeśli masz Perla w swoim Solarisie, użyj go. W przeciwnym razie ls z awk jest następnym najlepszym rozwiązaniem, ponieważ nie masz statystyk lub twoje znalezisko nie jest znalezieniem GNU.

ghostdog74
źródło
-3

W Solarisie jest sztuczka, której użyłem, jeśli poprosisz o rozmiar więcej niż jednego pliku, zwraca tylko całkowity rozmiar bez nazw - więc dołącz pusty plik, taki jak / dev / null jako drugi plik:

np. plik poleceń, którego chcesz / dev / null

Nie pamiętam, które polecenie rozmiaru działa dla ls / wc / etc - niestety nie mam skrzynki solaris, aby to przetestować.

Martin Beckett
źródło
-4

na Linuksie, którego możesz użyć du -h $FILE, czy to działa również na Solaris?

knittl
źródło
1
W rzeczywistości jednostki można konwertować, ale pokazuje to użycie dysku zamiast rozmiaru danych pliku („rozmiar pozorny”).
Palec
-7

Czy próbowałeś du -ks | awk '{print $ 1 * 1024}'. To może po prostu zadziałać.

Aditya
źródło
1
Pokazuje użycie dysku zamiast rozmiaru danych pliku („pozorny rozmiar”).
Palec