Pytanie Czy istnieje polecenie bash, które zlicza pliki?


Czy istnieje polecenie bash, które zlicza liczbę plików pasujących do wzorca?

Na przykład chcę uzyskać liczbę wszystkich plików w katalogu, który pasuje do tego wzorca: log*


76
2017-07-03 08:35


pochodzenie




Odpowiedzi:


Ten prosty jednolinijkowy powinien działać w dowolnej powłoce, a nie tylko w bashu:

ls -1q log* | wc -l

ls -1q da ci jedną linię na plik, nawet jeśli zawierają białe spacje lub znaki specjalne, takie jak znaki nowej linii.

Wyjście jest wyprowadzane do wc-l, które zlicza liczbę linii.


102
2017-07-03 08:41



Nie użyłbym -l, ponieważ to wymaga stat(2) na każdym pliku i do celów liczenia nic nie dodaje. - camh
Nie użyłbym ls, ponieważ tworzy proces potomny. log* jest rozszerzony przez powłokę, a nie ls, więc proste echo zrobiłaby. - cdarke
mywiki.wooledge.org/ParsingLs - ormaaj
Właściwie "ls -l" nie drukuje plików po jednym w linii, gdy wyjście jest wyprowadzone. touch abc$'\n'def; ls abc*def | wc -l drukuje 2, nawet jeśli nie potrzaskujesz, to się pokaże abc?def. ls -1f ma ten sam problem. - mogsie
@WalterTross To prawda (nie, że wydajność była wymogiem oryginalnego pytania). Zauważyłem też, że -q dba o pliki z nowymi liniami, nawet jeśli wyjście nie jest terminalem. Te flagi są obsługiwane przez wszystkie platformy i powłoki, na których testowałem. Aktualizuję odpowiedź, dzięki tobie i camh na wejście! - Daniel


Możesz to zrobić bezpiecznie (np. Nie będą one błędne przez pliki ze spacjami lub \n w ich nazwie) z bash:

$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
$ shopt -u nullglob

Musisz włączyć nullglob aby nie dostać dosłownie *.log w $logfiles  szyk jeśli żaden plik nie pasuje.


33
2017-07-03 08:43





Spróbuj tego:

echo *.log | wc -w


30
2017-07-03 08:41



echo *.log | wc -w dałoby nieprawidłowy wynik, jeśli niektóre pliki mają spacje w swoich nazwach - lanzz
@lanzz: True; nie pomyślał o tym! - Will Vousden
Nie mam plików ze spacjami - hudi
Choćby ty nie ma plików ze spacjami, inny użytkownik skryptu może napotkać złośliwie nazwany plik, powodując awarię skryptów. Ponadto, inni użytkownicy napotykają to na StackOverflow mogą mieć pliki z nowymi liniami i muszą znać pułapki. - mogsie


Wiele odpowiedzi tutaj, ale niektóre nie biorą pod uwagę

  • Nazwy plików ze spacjami, znakami nowej linii lub znakami kontrolnymi w nich
  • nazwy plików rozpoczynające się myślnikami (wyobrazić sobie plik o nazwie -l)
  • ukryte pliki, które zaczynają się od kropki (jeśli glob był *.log zamiast log*
  • katalogi pasujące do globu (np. katalog o nazwie logs to pasuje log*)
  • puste katalogi (tzn. wynik to 0)
  • bardzo duże katalogi (lista wszystkich może wyczerpać pamięć)

Oto rozwiązanie, które obsługuje wszystkie z nich:

ls 2>/dev/null -Ubad1 -- log* | wc -l

Wyjaśnienie:

  • -U przyczyny ls aby nie sortować wpisów, co oznacza, że ​​nie trzeba ładować całego katalogu w pamięci
  • -b drukuje ucieczki w stylu C dla niegraphic znaków, co powoduje drukowanie nowych linii jako \n.
  • -a wypisuje wszystkie pliki, nawet ukryte pliki (niekoniecznie potrzebne, gdy glob log* nie zawiera żadnych ukrytych plików)
  • -d wypisuje katalogi bez próby wyświetlenia listy zawartość katalogu, który jest co ls normalnie by to zrobił
  • -1 upewnia się, że jest na jednej kolumnie (ls robi to automatycznie podczas zapisu do potoku, więc nie jest to absolutnie konieczne)
  • 2>/dev/null przekierowuje stderr, więc jeśli istnieje 0 plików dziennika, zignoruj ​​komunikat o błędzie. (Zauważ, że shopt -s nullglob Mogłoby spowodować ls zamiast tego wyświetla cały katalog roboczy.)
  • wc -l zużywa listę katalogów podczas jej generowania, więc dane wyjściowe ls nigdy nie jest w pamięci w żadnym momencie.
  • -- Nazwy plików są oddzielane od polecenia za pomocą -- aby nie być rozumianym jako argumenty ls (w razie log* jest usunięty)

Muszla będzie rozszerzać log* do pełnej listy plików, które mogą wyczerpać pamięć, jeśli jest dużo plików, więc uruchomienie przez grep jest lepsze:

ls -Uba1 | grep ^log | wc -l

Ten ostatni obsługuje bardzo duże katalogi plików bez użycia dużej ilości pamięci (choć używa podpowłoki). The -d nie jest już konieczne, ponieważ zawiera tylko zawartość bieżącego katalogu.


29
2017-11-24 11:01



Świetna odpowiedź i niezwykle dokładne. - raratiru


Przyjęta odpowiedź na to pytanie jest błędna, ale mam niski ryps, więc nie mogę dodać do niego komentarza.

Prawidłowa odpowiedź na to pytanie jest podana przez Mat:

shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}

Problem z zaakceptowaną odpowiedzią polega na tym, że wc -l zlicza liczbę znaków nowej linii i zlicza je, nawet jeśli drukują do terminala jako "?" w wyniku działania 'ls -l'. Oznacza to, że zaakceptowana odpowiedź NIE SPEŁNIA, gdy nazwa pliku zawiera znak nowego wiersza. Przetestowałem sugerowane polecenie:

ls -l log* | wc -l

i błędnie zgłasza wartość 2, nawet jeśli istnieje tylko jeden plik pasujący do wzorca, którego nazwa zawiera znak nowego wiersza. Na przykład:

touch log$'\n'def
ls log* -l | wc -l

5
2017-10-30 06:11





Jeśli masz dużo plików i nie chcesz używać eleganckiego shopt -s nullglob i rozwiązanie tablicy bash, możesz użyć find i tak dalej, dopóki nie wydrukujesz nazwy pliku (która może zawierać znaki nowej linii).

find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l

Znajdzie to wszystkie pliki, które pasują do logu * i które nie zaczynają się od .* - "Nie nazwa. *" Jest zbyt duża, ale ważne jest, aby pamiętać, że domyślnie dla "ls" jest nie pokazywanie plików kropek, ale domyślną wartością dla find jest ich uwzględnianie.

Jest to poprawna odpowiedź i obsługuje wszystkie typy nazw plików, które można na nią rzutować, ponieważ nazwa pliku nigdy nie jest przekazywana między komendami.

Ale shopt nullglob odpowiedź to najlepsza odpowiedź!


4
2017-08-22 19:16



Prawdopodobnie powinieneś zaktualizować oryginalną odpowiedź, zamiast odpowiadać ponownie. - qodeninja
Myślę, że używam find vs użycie ls są dwa różne sposoby rozwiązania problemu. find nie zawsze jest obecny na maszynie, ale ls zwykle jest, - mogsie


Oto moja linijka do tego.

 file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)

2
2017-11-04 19:48