Esiste un'alternativa più concisa al piping al wc per il conteggio dei file in una directory


12

Se lo faccio ls -1 target_dir | wc -l, ottengo un conteggio dei file in una directory. Lo trovo un po 'ingombrante. C'è un modo più elegante o succinto?


2
Non è necessario "-1" quando si esegue il piping su wc.
Steve,

lsdà già il conteggio totale, quindi che ne dici ls -l | head -1? Trasformalo in un alias se vuoi qualcosa di più corto.
Daniel Wagner,

2
@DanielWagner L'output "total: nnn" ls -lindica la dimensione totale dei file, non il numero di file.
David Richerby,

2
Tieni presente che ls | wc -lti darà il conto sbagliato se i nomi dei file contengono nuove righe.
Chepner,

Dipende dal file system e conta le directory + 2 in una directory. La risposta ha 2 extra (poiché conta se stessa e il suo genitore). stat -c %h .fornisce le stesse informazioni dils -ld . | cut -d" " -f 2
ctrl-alt-delor

Risposte:


12

Supponendo bash 4+ (che ha una versione supportata di Ubuntu):

num_files() (
    shopt -s nullglob
    cd -P -- "${1-.}" || return
    set -- *
    echo "$#"
)

Chiamalo come num_files [dir]. dirè facoltativo, altrimenti utilizza la directory corrente. La tua versione originale non conta i file nascosti, quindi nemmeno questo. Se lo vuoi, shopt -s dotglobprima set -- *.

Il tuo esempio originale conta non solo i file regolari, ma anche le directory e altri dispositivi: se vuoi davvero solo file regolari (inclusi collegamenti simbolici a file regolari), dovrai controllarli:

num_files() (
    local count=0

    shopt -s nullglob
    cd -P -- "${1-.}" || return
    for file in *; do
        [[ -f $file ]] && let count++
    done
    echo "$count"
)

Se hai GNU find, qualcosa del genere è anche un'opzione (nota che include file nascosti, cosa che il tuo comando originale non ha fatto):

num_files() {
    find "${1-.}" -maxdepth 1 -type f -printf x | wc -c
}

(cambio -typea -xtypese anche voi volete contare link simbolici ai file regolari).


Non setfallirà se ci sono molti file? Penso che potresti dover usare un xargspo 'di codice di riepilogo per farlo funzionare nel caso generale.
l0b0

1
Inoltre, shopt -s dotglobse vuoi .che vengano contati i file che iniziano con
Trauma digitale

1
@ l0b0 Non credo setche fallirà in queste circostanze, dal momento che in realtà non lo stiamo facendo exec. Vale a dire, sul mio sistema, getconf ARG_MAXproduce 262144, ma se lo faccio test_arg_max() { set -- {1..262145}; echo $#; }; test_arg_max, risponde felicemente 262145.
Kojiro

@DavidRicherby -maxdepthnon è POSIX.
Chris Down,

4
@MichaelMartinez La scrittura di codice evidente non sostituisce la scrittura di codice corretto.
Chris Down,

3

f=(target_dir/*);echo ${#f[*]}

funziona correttamente per file con spazi, newline, ecc. nel nome.


puoi fornire qualche contesto? Questo dovrebbe andare in uno script bash?
codecowboy,

potrebbe. potresti anche inserirlo direttamente nella shell. quella versione presupponeva che volessi la directory corrente; l'ho modificato in modo che sia più vicino alla tua domanda. fondamentalmente crea una variabile di array di shell contenente tutti i file nella directory, quindi stampa il conteggio di quell'array. dovrebbe funzionare in qualsiasi shell con array - bash, ksh, zsh, ecc. - ma probabilmente non semplice sh / ash / dash.
Aaron Davies

2

lsè multi-colonne solo se viene emesso direttamente su un terminale, è possibile rimuovere l'opzione "-1", è possibile rimuovere l' wcopzione "-l", leggere solo il primo valore (soluzione pigra, da non utilizzare per prove legali, investigazioni criminali, operazioni critiche, operazioni tattiche ...).

ls target | wc 

5
Questo non riesce per i nomi di file contenenti newline.
l0b0

@Emmanuel Dovrai analizzare il risultato del tuo wcper ottenere il numero di file anche nel caso banale, quindi come è questa soluzione?
l0b0

@Emmanuel Questo può fallire se si targettratta di un glob che, una volta espanso, include alcune cose che iniziano con trattini. Ad esempio, crea una nuova directory, entra e fai touch -- {1,2,3,-a}.txt && ls *|wc(NB: usa rm -- *.txtper eliminare quei file.)
David Richerby,

Volevi dire wc -l? Altrimenti si ottengono conteggi newline, word e byte lsdell'output. Questo è ciò che David Richerby ha detto: devi analizzarlo di nuovo.
erik,

@erik Menziono wcsenza argomenti che non è necessario analizzare se il tuo cervello sa che il primo argomento è newline.
Emmanuel,

2

Se si tratta di concisione che stai dopo (piuttosto che esatto correttezza quando si tratta di file con i ritorni a capo nei loro nomi, etc.), vi consiglio solo di aliasing wc -la lc( "count line"):

$ alias lc='wc -l'
$ ls target_dir|lc

Come altri hanno notato, non è necessaria l' -1opzione ls, poiché è automatica quando lssi scrive su una pipe. (A meno che non ti sia stato consentito lsdi utilizzare sempre la modalità colonna. L'ho già visto prima, ma non molto spesso.)

Un lcalias è abbastanza utile in generale, e per questa domanda, se si guarda al caso "conta la directory corrente", ls|lcè il più succinto possibile.


2

Finora Aaron è l'unico approccio più succinto del tuo. Una versione più corretta del tuo approccio potrebbe apparire come:

ls -aR1q | grep -Ecv '^\./|/$|^$'

Ciò elenca in modo ricorsivo tutti i file, non le directory, uno per riga, inclusi i file .dot sotto la directory corrente, usando i globi di shell come necessario per sostituire i caratteri non stampabili. grep esclude qualsiasi elenco di directory principale o ... o * / o righe vuote - quindi dovrebbe esserci solo una riga per file - il conteggio totale di grep che ti restituisce. Se si desidera includere anche le directory secondarie:

ls -aR1q | grep -Ecv '^\.{1,2}/|^$'

Rimuovere l' -Runo o l'altro caso se non si desidera risultati ricorsivi.


1
Tendo a preferire fare quel genere di cose con find. Se vuoi solo un conteggio, questo dovrebbe funzionare: find -mindepth 1 -maxdepth 1 -printf '\n'|wc -l(rimuovi i controlli di profondità per ottenere risultati ricorsivi).
Aaron Davies,

@AaronDavies - che in realtà non funziona. Inserisci una nuova riga in uno di quei nomi di file e guarda di persona. Inoltre, per fare la stessa cosa in modo portabile fai: find . \! -name . -prune | wc -l- che ovviamente non funziona, ovviamente.
Mikeserv,

1
Non seguo: l' printfistruzione stampa una stringa costante (una nuova riga) che non include affatto il nome file, quindi i risultati sono indipendenti da qualsiasi strano nome di file. Questo trucco non può essere fatto affatto con un findche non supporta printf, ovviamente.
Aaron Davies,

@AaronDavies - oh, vero. Supponevo che il nome file fosse incluso. Ovviamente può essere fatto in modo portabile:find .//. \!. -name . -prune | grep -c '^\.//\.'
mikeserv

brillante! /essendo l'unico altro personaggio che non può apparire nei nomi dei file, la .//.sequenza è garantita per apparire esattamente una volta per ogni file, giusto? un paio di domande però: perché .//.e perché -prune? quando sarebbe diverso find . \! -name . | grep -c '^\.'? (Suppongo che il .tuo \!.sia un refuso.)
Aaron Davies,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.