Devo ispezionare tutte le sottodirectory e segnalare quanti file (senza ulteriore ricorsione) contengono:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Devo ispezionare tutte le sottodirectory e segnalare quanti file (senza ulteriore ricorsione) contengono:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Risposte:
Questo lo fa in modo sicuro e portatile. Non verrà confuso da strani nomi di file.
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done
Si noti che stamperà prima il numero di file, quindi il nome della directory su una riga separata. Se si desidera mantenere il formato OP, sarà necessario ulteriore formattazione, ad es
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'
Se hai un set specifico di sottodirectory che ti interessano, puoi sostituire il *
con quelle.
Perché è sicuro?(e quindi degno di script)
I nomi dei file possono contenere qualsiasi carattere tranne /
. Esistono alcuni caratteri che vengono trattati in modo speciale dalla shell o dai comandi. Questi includono spazi, newline e trattini.
Usando il for f in *
costrutto è un modo sicuro per ottenere ogni nome di file, indipendentemente da ciò che contiene.
Una volta che hai il nome file in una variabile, devi ancora evitare cose come find $f
. Se $f
contenesse il nome file -test
, find
si lamenterebbe dell'opzione che gli hai appena dato. Il modo per evitarlo è usando./
davanti al nome; in questo modo ha lo stesso significato, ma non inizia più con un trattino.
Newline e spazi sono anche un problema. Se $f
contiene "ciao amico" come nome file find ./$f
, è find ./hello, buddy
. Stai dicendo find
di guardare ./hello,
e buddy
. Se quelli non esistono, si lamenterà e non guarderà mai dentro ./hello, buddy
. Questo è facile da evitare: usa le virgolette intorno alle tue variabili.
Infine, i nomi dei file possono contenere nuove righe, quindi il conteggio delle nuove righe in un elenco di nomi di file non funzionerà; otterrai un conteggio extra per ogni nome file con una nuova riga. Per evitare ciò, non contare le nuove righe in un elenco di file; contate invece le righe (o qualsiasi altro carattere) che rappresentano un singolo file. Questo è il motivo per cui il find
comando ha semplicemente -exec echo \;
e non -exec echo {} \;
. Voglio solo stampare una nuova nuova riga allo scopo di calcolare i file.
-mindepth 1
-printf '\n'
posto di -exec echo
.
-printf
, ma non se vuoi che funzioni su FreeBSD, per esempio.
Supponendo che tu stia cercando una soluzione Linux standard, un modo relativamente semplice per raggiungere questo obiettivo è con find
:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
Dove find
attraversa le due sottodirectory specificate, fino a -maxdepth
1 che impedisce l'ulteriore ricorsione e riporta solo i file ( -type f
) separati da newline. Il risultato viene quindi reindirizzato wc
per contare il numero di tali righe.
find . -maxdepth 1 -type d
output?
find $dirs ...
oppure, (b) se si trovano esclusivamente nella directory di livello superiore, glob da quella directory,find */ ...
-exec echo
al tuo comando find - in questo modo non riecheggia il nome del file, solo una nuova riga.
Con "senza ricorsione", intendi che se directoryName1
ha delle sottodirectory, non vuoi contare i file nelle sottodirectory? In tal caso, ecco un modo per contare tutti i file regolari nelle directory indicate:
count=0
for d in directoryName1 directoryName2; do
for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
if [ -f "$f" ]; then count=$((count+1)); fi
done
done
Si noti che il -f
test svolge due funzioni: verifica se la voce corrispondente a uno dei globs sopra è un file normale e verifica se la voce era una corrispondenza (se uno dei globs non corrisponde a nulla, il modello rimane così com'è¹). Se si desidera contare tutte le voci nelle directory indicate indipendentemente dal tipo, sostituirle -f
con -e
.
Ksh ha un modo per far corrispondere i pattern ai file di punti e per produrre un elenco vuoto nel caso in cui nessun file corrisponda a un pattern. Quindi in ksh puoi contare file regolari come questo:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
o tutti i file semplicemente in questo modo:
FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}
Bash ha diversi modi per renderlo più semplice. Per contare i file regolari:
shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
Per contare tutti i file:
shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}
Come al solito, è ancora più semplice in zsh. Per contare i file regolari:
files=({directoryName1,directoryName2}/*(DN.))
count=$#files
Passare (DN.)
a (DN)
per contare tutti i file.
¹ Si noti che ogni modello corrisponde a se stesso, altrimenti i risultati potrebbero essere spenti (ad es. Se si contano i file che iniziano con una cifra, non si può fare semplicemente for x in [0-9]*; do if [ -f "$x" ]; then …
perché potrebbe esserci un file chiamato [0-9]foo
).
Basato su uno script di conteggio , la risposta di Shawn e un trucco di Bash per assicurarsi che anche i nomi di file con nuove righe siano stampati in una forma utilizzabile su una sola riga:
for f in *
do
if [ -d "./$f" ]
then
printf %q "$f"
printf %s ' '
find "$f" -maxdepth 1 -printf x | wc -c
fi
done
printf %q
è stampare una versione tra virgolette di una stringa, ovvero una stringa a riga singola che è possibile inserire in uno script Bash per essere interpretata come una stringa letterale comprendente (potenzialmente) newline e altri caratteri speciali. Ad esempio, vedere echo -n $'\tfoo\nbar'
vs printf %q $'\tfoo\nbar'
.
Il find
comando funziona semplicemente stampando un singolo carattere per ciascun file, quindi contando quelli invece di contare le righe.
Ecco un "forza bruta" via -ish per ottenere il risultato, utilizzando find
, echo
, ls
, wc
, xargs
e awk
.
find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
for i in *; do echo $i; ls $i | wc -l; done
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
find
quando Bash lo farà?(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done)
: per tutte le directory, conta il numero di voci in quella directory (inclusi i file con punti nascosti, esclusi.
e..
)