Risposte:
Questo semplice one-liner dovrebbe funzionare in qualsiasi shell, non solo bash:
ls -1q log* | wc -l
-1q ti darà una riga per file, anche se contengono spazi bianchi o caratteri speciali come newline.
L'output viene reindirizzato a wc -l, che conta il numero di righe.
ls, poiché crea un processo figlio. log*è espanso dalla shell, no ls, quindi un semplice echofarebbe.
logsnella directory in questione, verrà conteggiato anche il contenuto di quella directory dei registri. Questo probabilmente non è intenzionale.
Puoi farlo in modo sicuro (cioè non sarà disturbato da file con spazi o \nnel loro nome) con bash:
$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
È necessario attivare nullglobin modo che non si ottiene il valore letterale *.lognella $logfiles serie , se nessun file corrispondono. (Vedi Come "annullare" un 'set -x'? Per esempi su come ripristinarlo in sicurezza.)
shopt -u nullglobdeve essere ignorata se nullglobnon è stata disattivata, quindi hai iniziato.
*.logcon just *conterà le directory. Se i file che si desidera enumerare hanno la tradizionale convenzione di denominazione name.extension, utilizzare *.*.
Molte risposte qui, ma alcune non tengono conto
-l )*.loginvece dilog*logsche corrispondelog* )Ecco una soluzione che li gestisce tutti:
ls 2>/dev/null -Ubad1 -- log* | wc -l
Spiegazione:
-Ufa sì lsche le voci non vengano ordinate, il che significa che non è necessario caricare l'intero elenco di directory in memoria-bstampa le fughe in stile C per i caratteri non grafici, determinando in modo cruciale la stampa di nuove righe come \n.-astampa tutti i file, anche i file nascosti (non strettamente necessari quando il glob log*non implica file nascosti)-dstampa le directory senza tentare di elencare il contenuto della directory, cosa che lsnormalmente farebbe-1 si assicura che si trovi su una colonna (lo fa automaticamente quando si scrive su una pipe, quindi non è strettamente necessario)2>/dev/nullreindirizza stderr in modo che se ci sono 0 file di registro, ignora il messaggio di errore. (Nota che shopt -s nullglobpotrebbe causarels elencare l'intera directory di lavoro.)wc -lconsuma l'elenco delle directory mentre viene generato, quindi l'output di lsnon è mai in memoria in nessun momento.--I nomi dei file vengono separati dal comando usando in --modo da non essere compresi come argomenti ls(nel caso in cui log*venga rimosso)La shell si espanderà log*all'elenco completo dei file, il che potrebbe esaurire la memoria se contiene molti file, quindi è meglio eseguirlo attraverso grep:
ls -Uba1 | grep ^log | wc -l
Quest'ultimo gestisce directory di file estremamente grandi senza usare molta memoria (sebbene utilizzi una sottoshell). Non -dè più necessario, poiché elenca solo i contenuti della directory corrente.
Per una ricerca ricorsiva:
find . -type f -name '*.log' -printf x | wc -c
wc -cconterà il numero di caratteri nell'output di find, mentre -printf xindica finddi stampare un singolo xper ogni risultato.
Per una ricerca non ricorsiva, procedere come segue:
find . -maxdepth 1 -type f -name '*.log' -printf x | wc -c
-name '*.log', conterà tutti i file, che è quello che mi serviva per il mio caso d'uso. Anche il flag -maxdepth è estremamente utile, grazie!
find; basta stampare qualcosa di diverso dal nome del file alla lettera.
La risposta accettata per questa domanda è sbagliata, ma ho un rappresentante basso, quindi non posso aggiungere un commento.
La risposta corretta a questa domanda è data da Mat:
shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}
Il problema con la risposta accettata è che wc -l conta il numero di caratteri di nuova riga e li conta anche se stampano sul terminale come "?" nell'output di 'ls -l'. Ciò significa che la risposta accettata NON RIUSCE quando un nome file contiene un carattere di nuova riga. Ho testato il comando suggerito:
ls -l log* | wc -l
e riporta erroneamente un valore di 2 anche se c'è solo 1 file corrispondente allo schema il cui nome sembra contenere un carattere di nuova riga. Per esempio:
touch log$'\n'def
ls log* -l | wc -l
Se hai molti file e non vuoi usare la shopt -s nullglobsoluzione array elegante e bash, puoi usare find e così via finché non stampi il nome del file (che potrebbe contenere newline).
find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l
Questo troverà tutti i file che corrispondono al registro * e che non iniziano con .* - Il "non nome. *" È ridondante, ma è importante notare che il valore predefinito per "ls" non è mostrare i file punto, ma il valore predefinito perché trovare è includerli.
Questa è una risposta corretta e gestisce qualsiasi tipo di nome file che puoi lanciarvi, perché il nome file non viene mai passato tra i comandi.
Ma la shopt nullglobrisposta è la migliore risposta!
findvs usare lssiano due modi diversi di risolvere il problema. findnon è sempre presente su una macchina, ma di lssolito lo è,
findprobabilmente non ha tutte quelle opzioni fantasiose per lsentrambi.
-maxdepth 1
findfa questo di default. Questo può creare confusione se non ci si rende conto che esiste una cartella figlio nascosta e può essere vantaggioso da utilizzare lsin alcune circostanze, che non riporta i file nascosti per impostazione predefinita.
Ecco la mia unica fodera per questo.
file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)
set -- non sta facendo altro che prepararci $#, che memorizza il numero di argomenti della riga di comando che sono stati passati al programma della shell
(reputazione insufficiente per commentare)
Questo è BUGGY :
ls -1q some_pattern | wc -l
Se shopt -s nullglobsembra impostato, stampa il numero di TUTTI i file regolari, non solo quelli con il modello (testato su CentOS-8 e Cygwin). Chissà cosa fanno altri bug insignificantils ha?
Questo è CORRETTO e molto più veloce:
shopt -s nullglob; files=(some_pattern); echo ${#files[@]};
Fa il lavoro previsto.
0.006su CentOS e 0.083su Cygwin (nel caso sia usato con cura).
0.000su CentOS e 0.003su Cygwin.
È possibile definire facilmente un tale comando, usando una funzione shell. Questo metodo non richiede alcun programma esterno e non genera alcun processo figlio. Non tenta di lsanalizzare in modo pericoloso e gestisce i caratteri "speciali" (spazi bianchi, nuove righe, barre rovesciate e così via) bene. Si basa solo sul meccanismo di espansione del nome file fornito dalla shell. È compatibile con almeno sh, bash e zsh.
La riga seguente definisce una funzione chiamata countche stampa il numero di argomenti con cui è stata chiamata.
count() { echo $#; }
Chiamalo semplicemente con il modello desiderato:
count log*
Affinché il risultato sia corretto quando il modello globbing non ha corrispondenza, l'opzione shell nullglob(o failglob- che è il comportamento predefinito su zsh) deve essere impostata al momento dell'espansione. Può essere impostato in questo modo:
shopt -s nullglob # for sh / bash
setopt nullglob # for zsh
A seconda di cosa vuoi contare, potresti anche essere interessato all'opzione shell dotglob.
Sfortunatamente, almeno con bash, non è facile impostare queste opzioni localmente. Se non si desidera impostarli a livello globale, la soluzione più semplice è utilizzare la funzione in questo modo più contorto:
( shopt -s nullglob ; shopt -u failglob ; count log* )
Se vuoi recuperare la sintassi leggera count log*, o se vuoi davvero evitare di generare una subshell, puoi hackerare qualcosa sulla falsariga di:
# sh / bash:
# the alias is expanded before the globbing pattern, so we
# can set required options before the globbing gets expanded,
# and restore them afterwards.
count() {
eval "$_count_saved_shopts"
unset _count_saved_shopts
echo $#
}
alias count='
_count_saved_shopts="$(shopt -p nullglob failglob)"
shopt -s nullglob
shopt -u failglob
count'
Come bonus, questa funzione è di uso più generale. Per esempio:
count a* b* # count files which match either a* or b*
count $(jobs -ps) # count stopped jobs (sh / bash)
Trasformando la funzione in un file di script (o un programma C equivalente), richiamabile dal PATH, può anche essere composta con programmi come finde xargs:
find "$FIND_OPTIONS" -exec count {} \+ # count results of a search
Ho pensato molto a questa risposta, soprattutto considerando le cose da non perdere . All'inizio ci ho provato
<WARNING! NON FUNZIONA>
du --inodes --files0-from=<(find . -maxdepth 1 -type f -print0) | awk '{sum+=int($1)}END{print sum}'
</ ATTENZIONE! NON FUNZIONA>
che ha funzionato se esistesse solo un nome file come
touch $'w\nlf.aa'
ma fallisco se ho creato un nome file come questo
touch $'firstline\n3 and some other\n1\n2\texciting\n86stuff.jpg'
Alla fine ho pensato a quello che sto mettendo di seguito. Nota Stavo cercando di ottenere un conteggio di tutti i file nella directory (non includere alcuna sottodirectory). Penso che, insieme alle risposte di @Mat e @Dan_Yard, oltre ad avere almeno la maggior parte dei requisiti stabiliti da @mogsie (non sono sicuro della memoria.) Penso che la risposta di @mogsie sia corretta, ma cerco sempre di stare lontano dall'analisi a lsmeno che non sia una situazione estremamente specifica.
awk -F"\0" '{print NF-1}' < <(find . -maxdepth 1 -type f -print0) | awk '{sum+=$1}END{print sum}'
Più facilmente:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -print0) | \
awk '{sum+=$1}END{print sum}'
Questo sta facendo una ricerca specifica per i file, delimitando l'output con un carattere null (per evitare problemi con spazi e avanzamenti di riga), quindi contando il numero di caratteri null. Il numero di file sarà uno in meno rispetto al numero di caratteri null, poiché alla fine ci sarà un carattere null.
Per rispondere alla domanda del PO, ci sono due casi da considerare
1) Ricerca non ricorsiva:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
2) Ricerca ricorsiva. Nota che -namepotrebbe essere necessario modificare il contenuto del parametro per comportamenti leggermente diversi (file nascosti, ecc.).
awk -F"\0" '{print NF-1}' < \
<(find . -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
Se qualcuno desidera commentare come queste risposte sono paragonabili a quelle che ho menzionato in questa risposta, per favore fallo.
Nota, ho ottenuto questo processo di pensiero mentre ottenevo questa risposta .
Ecco cosa faccio sempre:
ls log * | awk 'END {print NR}'
awk 'END{print NR}'dovrebbe essere equivalente a wc -l.
ls -1 log* | wc -l
Il che significa elencare un file per riga e quindi reindirizzarlo al comando conteggio parole con il parametro che passa alle righe di conteggio.
-l, poiché ciò richiedestat(2)ogni file e ai fini del conteggio non aggiunge nulla.