2012-12-05 4 views
12

Sto imparando lo script di bash e ho scritto uno script per contare i file e le directory nella directory fornita come argomento. Lo faccio funzionare in un modo che mi sembra strano e mi chiedo se esiste un modo più semplice per farlo.Conteggiare file e directory utilizzando lo script di shell

Ho commentato il codice che funzionerà, ma l'ho lasciato come confronto. Sto cercando di ottenere il funzionamento di for, utilizzando invece le istruzioni if al suo interno per rilevare se un elemento nel percorso specificato è un file o una directory.

Modifica: Ho appena scoperto che il codice commentato conta tutti i file e le directory nelle sottodirectory della posizione specificata! C'è un modo per prevenire questo e basta contare i file e le directory della posizione data?

#!/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 

Per qualche motivo l'uscita del for -loop, non importa dove io punto la posizione, torna sempre:

File count: 0 , Directory count: 1 
+0

L'intero script è rotto se ci sono spazi nei nomi dei file. Mentre stai ascoltando, il mio primo consiglio è: Usa più citazioni! –

+0

Un altro commento: posso dire che le tue fonti per l'apprendimento di bash sono obsolete e non mostrano buone pratiche. –

+0

L'estensione del nome del file considera la lettura: http://bash.cyberciti.biz/bash-reference-manual/Filename-Expansion.html – hovanessyan

risposta

4

Lei non è iterare su l'elenco dei file all'interno del data directory; aggiungi /* dopo il $LOCATION. Lo script dovrebbe essere simile:

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

Come sottolineato da dogbane, semplicemente aggiungendo /* conteranno solo i file che non inizia con .; per farlo, si deve effettuare le seguenti operazioni:

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

Questo è il vero problema principale nello script. – Jite

+1

Questa è la risposta che sto cercando! Grazie. Le altre soluzioni qui hanno contato tutti i file e le directory delle sottodirectory della posizione, cosa che non volevo. Saluti. –

+1

Buon punto. Forse un ciclo separato con $ LOCATION /.* ?? –

10

Per risolvere semplicemente il problema è possibile utilizzare:

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

find cercherà tutti i file (-type f) o directory (-type d) ricorsivamente sotto $LOCATION ; wc -l conterrà il numero di righe scritte sullo stdout in ciascun caso.

Tuttavia, se si vuole imparare, lo script di bash potrebbe essere un modo migliore. Alcuni commenti:

  • Se si desidera cercare file/directory in $LOCATION solo (non ricorsivamente sotto i loro sottodirectory ecc), è possibile utilizzare for item in $LOCATION/*, dove il * si espanderà per l'elenco dei file/directory in $LOCATION directory. Il * mancante è il motivo per cui lo script originale restituisce 0/1 (perché la sola directory $LOCATION è l'unica conteggiata).
  • Si consiglia di verificare prima che $LOCATION sia in realtà una directory con [ -d $LOCATION ].
  • Per espressioni aritmetiche, utilizzare $((...)), ad esempio FILECOUNT=$((FILECOUNT + 1)).
  • Se si desidera trovare tutti i file/directory in modo ricorsivo, è possibile combinare find con un ciclo.

Esempio:

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

Sì, il codice $ LOCATION/* ha funzionato perfettamente. Perché $ ((...)) invece di $ []? Prestazioni correlate? –

+0

@AlanSmith, '$ [...]' è una sintassi deprecata, non riesco nemmeno a trovarlo in 'man bash'. La versione moderna è '$ ((...))' (anche se sembrano funzionare allo stesso modo). Per il tuo compito potresti anche usare '((++ FILECOUNT))'. Vedi http://stackoverflow.com/questions/2188199/bash-double-or-single-bracket-parentheses-curly-braces. –

+0

Ok, ottime informazioni, grazie. –

2

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

Se lo dici tu;)

In alternativa, ridurre lo script per

find /path/to/directory | wc -l 

Per directory corrente, fare:

find . | wc -l 
+0

Nota: Contiene anche '.' come file. Quindi con solo due file regolari che suppongo tu voglia contare, questo stamperà '3'. – Jite

+0

Sta cercando un modo per distinguere i conteggi di dir e file - ecco perché ha 2 contatori nel suo codice. La tua ricerca fornisce il conteggio totale, senza distinguere tra i tipi di file. – hovanessyan

1

Usa

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

Per il conteggio dei file, e

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

Per il conteggio delle directory

12

Usa find come illustrato di seguito. Questa soluzione conterà correttamente nomi di file con spazi, newline e dotfile.

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

Si noti che il DIRCOUNT include la directory corrente (.). Se non vuoi questo, sottrai 1.

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

Questo è ovviamente il miglior metodo possibile in questo caso particolare! Come menzionato da dogbane, gestisce tutti i possibili casi di file con simboli divertenti nel loro nome, e funziona bene anche con le directory che contengono molti file (che non è il caso delle soluzioni bash che utilizzano globbing). Una cosa, però, il mio 'find' si lamenta di' -maxdepth 1' dato dopo l'opzione '-type f'. (Un'altra cosa, le virgolette non sono necessarie qui, ma non fa mai male usarle). –