La mia risposta tldr è:
function emptydir {
[ "$1/"* "" = "" ] 2> /dev/null &&
[ "$1/"..?* "" = "" ] 2> /dev/null &&
[ "$1/".[^.]* "" = "" ] 2> /dev/null ||
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
È conforme a POSIX e, non importa molto, in genere è più veloce della soluzione che elenca la directory e reindirizza l'output a grep.
Uso:
if emptydir adir
then
echo "nothing found"
else
echo "not empty"
fi
Mi piace la risposta https://unix.stackexchange.com/a/202276/160204 , che riscrivo come:
function emptydir {
! { ls -1qA "./$1/" | grep -q . ; }
}
Elenca la directory e reindirizza il risultato a grep. Invece, propongo una semplice funzione che si basa sull'espansione e sul confronto globale.
function emptydir {
[ "$(shopt -s nullglob; echo "$1"/{,.[^.],..?}*)" = "" ]
}
Questa funzione non è POSIX standard e chiama una subshell con $()
. Spiego prima questa semplice funzione in modo che possiamo capire meglio la soluzione finale (vedi la risposta tldr sopra) in seguito.
Spiegazione:
Il lato sinistro (LHS) è vuoto quando non si verifica alcuna espansione, come nel caso della directory vuota. L'opzione nullglob è obbligatoria perché altrimenti quando non vi è corrispondenza, il glob stesso è il risultato dell'espansione. (Avere l'RHS corrispondente ai globs dell'LHS quando la directory è vuota non funziona a causa dei falsi positivi che si verificano quando un glob LHS corrisponde a un singolo file chiamato glob stesso: il *
nel glob corrisponde alla sottostringa *
nel nome del file. ) L'espressione parentesi graffa {,.[^.],..?}
copre i file nascosti, ma non ..
oppure .
.
Poiché shopt -s nullglob
viene eseguito all'interno $()
(una subshell), non cambia l' nullglob
opzione della shell corrente, che di solito è una buona cosa. D'altra parte, è una buona idea impostare questa opzione negli script, perché è soggetto a errori che un glob restituisca qualcosa quando non c'è corrispondenza. Quindi, si potrebbe impostare l'opzione nullglob all'inizio dello script e non sarà necessario nella funzione. Ricordiamolo: vogliamo una soluzione che funzioni con l'opzione nullglob.
Avvertenze:
Se non abbiamo accesso in lettura alla directory, la funzione riporta lo stesso di una directory vuota. Questo vale anche per una funzione che elenca la directory e grep l'output.
Il shopt -s nullglob
comando non è POSIX standard.
Utilizza la subshell creata da $()
. Non è un grosso problema, ma è bello se possiamo evitarlo.
Pro:
Non che sia davvero importante, ma questa funzione è quattro volte più veloce della precedente, misurata con la quantità di tempo della CPU speso nel kernel all'interno del processo.
Altre soluzioni:
Siamo in grado di rimuovere il non POSIX shopt -s nullglob
dei comandi sul lato sinistro e inserire la stringa "$1/* $1/.[^.]* $1/..?*"
nel RHS ed eliminare separatamente i falsi positivi che si verificano quando abbiamo solo i file denominati '*'
, .[^.]*
o ..?*
nella directory:
function emptydir {
[ "$(echo "$1"/{,.[^.],..?}*)" = "$1/* $1/.[^.]* $1/..?*" ] &&
[ ! -e "$1/*" ] && [ ! -e "$1/.[^.]*" ] && [ ! -e "$1/..?*" ]
}
Senza il shopt -s nullglob
comando, ora ha senso rimuovere la subshell, ma dobbiamo stare attenti perché vogliamo evitare la divisione delle parole e tuttavia consentire l'espansione globale su LHS. In particolare, la citazione per evitare la divisione delle parole non funziona, perché impedisce anche l'espansione globale. La nostra soluzione è considerare i globs separatamente:
function emptydir {
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
Abbiamo ancora una suddivisione delle parole per il singolo glob, ma ora va bene, perché si tradurrà in un errore solo quando la directory non è vuota. Abbiamo aggiunto 2> / dev / null, per scartare il messaggio di errore quando ci sono molti file corrispondenti al glob dato sull'LHS.
Ricordiamo che vogliamo una soluzione che funzioni anche con l'opzione nullglob. La soluzione precedente non riesce con l'opzione nullglob, perché quando la directory è vuota, anche l'LHS è vuoto. Fortunatamente, non dice mai che la directory è vuota quando non lo è. Non riesce a dire che è vuoto quando lo è. Quindi, possiamo gestire l'opzione nullglob separatamente. Non possiamo semplicemente aggiungere i casi [ "$1/"* = "" ]
ecc. Perché questi si espandono come [ = "" ]
, ecc. Che sono sintatticamente errati. Quindi, [ "$1/"* "" = "" ]
invece usiamo ecc. Abbiamo ancora una volta considerare i tre casi *
, ..?*
e .[^.]*
di abbinare i file nascosti, ma non .
e..
. Questi non interferiranno se non abbiamo l'opzione nullglob, perché non dicono mai che è vuota quando non lo è. Quindi, la soluzione finale proposta è:
function emptydir {
[ "$1/"* "" = "" ] 2> /dev/null &&
[ "$1/"..?* "" = "" ] 2> /dev/null &&
[ "$1/".[^.]* "" = "" ] 2> /dev/null ||
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
Problemi di sicurezza:
Creare due file rm
e x
in una directory vuota ed eseguire *
sul prompt. Il glob *
si espanderà rm x
e questo verrà eseguito per rimuoverlo x
. Questo non è un problema di sicurezza, perché nella nostra funzione, i globs si trovano in cui le espansioni non sono viste come comandi, ma come argomenti, proprio come in for f in *
.