Policz pliki w katalogu według rozszerzenia

15

Do celów testowych chciałbym policzyć, ile plików obrazów znajduje się w katalogu, oddzielając każdy typ pliku obrazu według rozszerzenia pliku (jpg = „tak”. To dlatego, że później przyda się w innym skrypcie, który wykona akcję na każdym rozszerzeniu pliku). Czy mogę użyć czegoś takiego jak poniżej tylko do plików JPEG?

jpg=""
count=`ls -1 *.jpg 2>/dev/null | wc -l`
if [ $count != 0 ]
then
echo jpg files found: $count ; jpg="yes"
fi

Biorąc pod uwagę rozszerzenia plików jpg, png, bmp, raw i inne, czy powinienem to whilezrobić?

watchmansky
źródło

Odpowiedzi:

14

Sugerowałbym inne podejście, unikając możliwych problemów związanych z dzieleniem słów ls

#!/bin/bash

shopt -s nullglob

for ext in jpg png gif; do 
  files=( *."$ext" )
  printf 'number of %s files: %d\n' "$ext" "${#files[@]}"

  # now we can loop over all the files having the current extension
  for f in "${files[@]}"; do
    # anything else you like with these files
    :
  done 

done

Możesz zapętlić filestablicę dowolnymi innymi poleceniami, które chcesz wykonać na plikach poszczególnych rozszerzeń.


Bardziej przenośnie - lub w przypadku powłok, które nie udostępniają jawnie tablic - można ponownie użyć tablicy parametrów pozycyjnych powłoki, tj.

set -- *."$ext"

a następnie zamień ${#files[@]}i za ${files[@]}pomocą $#i"$@"

steeldriver
źródło
23

Moje podejście to:

  1. Wyświetl wszystkie pliki w katalogu
  2. Wyodrębnij ich rozszerzenie
  3. Posortuj wynik
  4. Policz wystąpienia każdego rozszerzenia

Coś w tym stylu (ostatnie awkwywołanie służy wyłącznie do formatowania):

ls -q -U | awk -F . '{print $NF}' | sort | uniq -c | awk '{print $2,$1}'

(zakładając, że GNU ma lstutaj -Uopcję pomijania sortowania jako optymalizację. Można go bezpiecznie usunąć bez wpływu na funkcjonalność, jeśli nie jest obsługiwana).

groxxda
źródło
mhmh ... później powinienem filtrować każde znalezione rozszerzenie, aby wykonać dla niego akcję?
watchmansky
To zależy od tego, co ostatecznie chcesz zrobić. Czy możesz podać więcej informacji?
groxxda
Mój cel: skrypt przetwarzający każdy plik rozszerzenia (tylko plik obrazu) zmieniający rozmiar na podstawie danych wejściowych użytkownika. Zaczynam od liczby plików jpg, następnego png itp.
watchmansky
rozwiązanie steeldrivers może być bardziej odpowiednie.
groxxda
2
Miałem zarówno JPGi jpgpliki, i chciał go rekurencyjnie więc moje rozwiązanie było napisaćfind . -type f | awk -F . '{print tolower($NF)}' | sort | uniq -c | awk '{print $2,":",$1}'
Kristian
11

Rekurencyjnie przegląda pliki i zlicza pasujące rozszerzenia:

$ find . -type f | sed -e 's/.*\.//' | sort | uniq -c | sort -n | grep -Ei '(tiff|bmp|jpeg|jpg|png|gif)$'
   6 tiff
   7 bmp
  26 jpeg
  38 gif
  51 jpg
  54 png
Zestaw
źródło
6
find -type f | sed -e 's/.*\.//' | sort | uniq -c
Neik
źródło
3
Nie zapomnij katalogu początkowego z find. Może również pomóc przyszłym czytelnikom tych odpowiedzi, jeśli podasz krótkie wyjaśnienie swojego rozwiązania (na wypadek, gdyby chcieli zmodyfikować go w nieco innym przypadku).
Jeff Schaller
Jak dobrze to rozwiązanie radzi sobie z nazwami ścieżek zawierających spacje? Newlines?
dhag
1
finddomyślnie jest to bieżący katalog, i właśnie z tego korzystam. Nie sądzę, żeby Bóg chciał, aby w nazwach plików były spacje, ale w tym przypadku działa to dobrze. Jeśli masz nowe linie, to zasługujesz na wszystko, co dostajesz. Pomyślałem o wyjaśnieniu, ale zdecydowałem, że odpowiedź będzie zbyt długa, myślę, że liczy się prostota. 99% przypadków w 1% przypadków. Prawdopodobnie jest to zgodne z wersją 7.
Neik,
3

Może może być krótszy

exts=( *.jpg *.png *.gif ); printf "There are ${#exts[@]}" extensions;
Valentin Bajrami
źródło
3

Wszystko, co się wiąże, lsmoże przynieść nieoczekiwane rezultaty ze specjalnymi znakami (spacja i inne symbole). Każdy bashizm (jak tablice) nie jest przenośny. Wszystko, co się wiąże, while readjest zwykle powolne.

Z drugiej strony findjest BARDZO elastyczny (wiele opcji do filtrowania), ma [przynajmniej] dwie składnie, które są bezpieczne dla specjalnych znaków ... i dobrze skaluje się w dużym katalogu.

W tym przykładzie użyłem -inamedopasowania nazwy rozszerzenia zarówno wielkich, jak i małych liter. Ograniczyłem również -maxdepth 1respektowanie pytania „w bieżącym katalogu”. Zamiast zliczać liczbę wierszy, w których nazwy plików mogą zawierać CR / LF, -print0wydrukuje bajt NULL na końcu każdej nazwy pliku ... tak samo | tr -d -c "\000" | wc -ldokładnie zlicza pliki (bajty NULL!).

extensions="jpg png gif"
for ext in $extensions; do
  c=$(find . -maxdepth 1 -iname "*.$ext" -print0 | tr -d -c "\000" | wc -c)
  if [ $c -gt 0 ]; then
    echo "Found $c  *.$ext files"

    find . -maxdepth 1 -iname "*.$ext" -print0 | xargs -0 -r -n1 DOSOMETHINGHERE
    # or #  find . -maxdepth 1 -iname "*.$ext" -exec "ls" "-l" "{}" ";"
  fi
done

PS -print0 | tr -d -c "\000" | wc -cmożna zastąpić -printf "\000" | wc -club nawet -printf '\n' | wc -l.

Franklin Piat
źródło
0

mogę po prostu użyć ls do czegoś takiego prostego IMO

ls -l /opt/ssl/certs/*.pem | wc -l

lub

count=$(ls -l /some/folder/*.jpg | wc -l)

lub

ls *.{mp3,exe,mp4} 2>/dev/null | wc -l
Mike Q
źródło
-2

Jeśli jesteś pewien rozszerzenia, możesz przejść z findpodobnymi

find *.jpeg | wc -l
Nithish JV
źródło
dopóki ktoś nie stworzy touch $'foo\nbar.jpegi nie zostanie policzony dwukrotnie zamiast raz. Lub gorzej, ktoś to robimkdir directory.jpeg; touch directory.jpeg/{1..100}.txt
Jeff Schaller