2012-12-05 5 views
12

Uczę się skryptowania bash i napisałem skrypt, aby policzyć pliki i katalogi w katalogu dostarczonym jako argument. Działa to w jedną stronę, co wydaje mi się dziwne i zastanawiam się, czy istnieje prostszy sposób robienia tego.Policz pliki i katalogi przy użyciu skryptu powłoki

Skomentowałem kod, który zadziała, ale pozostawił go jako porównanie. Próbuję uzyskać poprawne działanie for, używając w tym celu instrukcji if, aby wykryć, czy element w danej lokalizacji jest plikiem lub katalogiem.

Edytuj: Właśnie się dowiedziałem, że skomentowany kod zlicza wszystkie pliki i katalogi w podkatalogach danej lokalizacji! Czy istnieje sposób, aby temu zapobiec i po prostu policzyć pliki i katalogi danej lokalizacji?

#!/bin/bash 

LOCATION=$1 
FILECOUNT=0 
DIRCOUNT=0 

if [ "$#" -lt "1" ] 
then 
    echo "Usage: ./test2.sh <directory>" 
    exit 0 
fi 

#DIRS=$(find $LOCATION -type d) 
#FILES=$(find $LOCATION -type f) 

#for d in $DIRS 
#do 
# DIRCOUNT=$[$DIRCOUNT+1] 
#done 

#for f in $FILES 
#do 
# FILECOUNT=$[$FILECOUNT+1] 
#done 

for item in $LOCATION 
do 
if [ -f "$item" ] 
    then 
     FILECOUNT=$[$FILECOUNT+1] 
    elif [ -d "$item" ] 
     then 
     DIRCOUNT=$[$DIRCOUNT+1] 
fi 
done 

echo "File count: " $FILECOUNT 
echo "Directory count: " $DIRCOUNT 

Z jakiegoś powodu wyjście for -loop, bez względu na to gdzie mogę wskazać lokalizację, zawsze powraca:

File count: 0 , Directory count: 1 
+0

Twój cały skrypt jest uszkodzony, jeśli w nazwach plików występują spacje. Jak się domyślacie, moją pierwszą radą jest: Użyj więcej cytatów! –

+0

Kolejna uwaga: mogę powiedzieć, że twoje źródła do nauki bash są nieaktualne i nie pokazują dobrych praktyk. –

+0

Po rozszerzeniu nazwy pliku rozważ czytanie: http://bash.cyberciti.biz/bash-reference-manual/Filename-Expansion.html – hovanessyan

Odpowiedz

4

Nie jesteś iteracji nad listą plików wewnątrz dana informator; dodaj /* po $LOCATION. Twój skrypt powinien wyglądać następująco:

... 
for item in $LOCATION/* 
do 
... 

Jak wskazano przez dogbane, po prostu dodając /* będą się liczyć tylko te pliki, które nie zaczynają się .; za to, powinien wykonać następujące czynności:

... 
for item in $LOCATION/* $LOCATION/.* 
do 
... 
+2

To faktyczny główny problem w skrypcie. – Jite

+1

Oto odpowiedź, której szukam! Dzięki. Pozostałe rozwiązania uwzględniały również pliki i katalogi podkatalogów lokalizacji, których nie chciałem. Twoje zdrowie. –

+1

Dobra uwaga. Może osobna pętla z $ LOCATION /.* ?? –

10

po prostu rozwiązać ten problem można użyć:

FILECOUNT=$(find $LOCATION -type f | wc -l) 
DIRCOUNT=$(find $LOCATION -type d | wc -l) 

find będzie szukać wszystkich plików (-type f) lub katalogów (-type d) rekurencyjnie pod $LOCATION ; wc -l policzy liczbę linii zapisanych na standardowe wyjście w każdym przypadku.

Jednak jeśli chcesz się nauczyć, skrypt basha może być lepszym sposobem. Niektóre komentarze:

  • Jeśli chcesz szukać plików/katalogów w $LOCATION tylko (nie rekursywnie pod ich podkatalogów itp), można użyć for item in $LOCATION/*, gdzie * rozwinie się lista plików/katalogów w $LOCATION informator. Brakujący * powoduje, że oryginalny skrypt zwraca 0/1 (ponieważ sam katalog jest liczony jako jedyny).
  • Najpierw sprawdź, czy $LOCATION jest katalogiem z [ -d $LOCATION ].
  • Dla wyrażeń arytmetycznych użyj $((...)), na przykład FILECOUNT=$((FILECOUNT + 1)).
  • Jeśli chcesz znaleźć rekursywnie wszystkie pliki/katalogi, możesz połączyć pętlę z find.

Przykład:

find $LOCATION | while read item; do 
    # use $item here... 
done 
+0

Tak, kod $ LOCATION/* działał idealnie. Dlaczego $ ((...)) zamiast $ []? Związane z wydajnością? –

+0

@AlanSmith, '$ [...]' jest przestarzałą składnią, nie mogę jej nawet znaleźć w 'man bash'. Nowoczesna wersja to '$ ((...))' (choć zdają się działać w ten sam sposób). Do Twojego zadania możesz użyć '((++ FILECOUNT))'. Zobacz http://stackoverflow.com/questions/2188199/bash-double-or-single-bracket-parentheses-curly-braces. –

+0

OK, świetne informacje, dzięki. –

2

... am wondering if there is a simpler way of doing it.

Skoro tak mówisz;)

Ewentualnie zmniejszyć skrypt

find /path/to/directory | wc -l 

W bieżącym katalogu, należy:

find . | wc -l 
+0

Uwaga: Zlicza również plik '.' jako plik. Tak więc z tylko dwoma zwykłymi plikami, które jak sądzę chcesz policzyć, wyświetli się '3'. – Jite

+0

Szuka sposobu na rozróżnienie liczby katalogów i plików - dlatego ma w swoim kodzie 2 liczniki. Twoje wyszukiwanie daje całkowitą liczbę, nie rozróżniając typów plików. – hovanessyan

1

Zastosowanie

find $LOCATION -maxdepth 1 -type f | wc -l 

dla liczby plików, a

find $LOCATION -maxdepth 1 -type d | wc -l 

do liczenia katalogów

12

Zastosowanie find jak pokazano poniżej. To rozwiązanie będzie poprawnie liczyć nazwy plików ze spacjami, znakami nowej linii i kropkami.

FILECOUNT="$(find . -type f -maxdepth 1 -printf x | wc -c)" 
DIRCOUNT="$(find . -type d -maxdepth 1 -printf x | wc -c)" 

Zauważ, że DIRCOUNT zawiera aktualny katalog (.). Jeśli tego nie chcesz, odejmij 1.

((DIRCOUNT--)) # to exclude the current directory 
+0

Jest to oczywiście najlepsza z możliwych metod w tym konkretnym przypadku! Jak wspomina dogbane, obsługuje wszystkie możliwe przypadki plików mających śmieszne symbole w swoim imieniu, a także dobrze działa z katalogami zawierającymi wiele plików (co nie ma miejsca w przypadku rozwiązań bash używających globowania). Jedną rzeczą jest to, że moje 'find' narzeka na' -maxdepth 1' podaną po opcji '-type f'. (Inna rzecz, podwójne cytaty nie są tu konieczne, ale nigdy nie boli ich używać). –