Bash scripting: test per directory vuota


95

Voglio verificare se una directory non contiene alcun file. In tal caso, salterò alcune elaborazioni.

Ho provato quanto segue:

if [ ./* == "./*" ]; then
    echo "No new file"
    exit 1
fi

Questo dà il seguente errore:

line 1: [: too many arguments

C'è una soluzione / alternativa?


Risposte:


121
if [ -z "$(ls -A /path/to/dir)" ]; then
   echo "Empty"
else
   echo "Not Empty"
fi

Inoltre, sarebbe bello controllare se la directory esiste prima.


11
Non usare &&e ||contemporaneamente! Se echo "Not Empty"fallisce, echo "Empty"verrà eseguito! Prova echo "test" && false || echo "fail"! Sì, lo so echoche non fallirà, ma se cambi altri comandi, sarai sorpreso!
uzsolt,

4
Fornisci almeno un esempio quando il codice sopra non funziona. Concretamente questo codice è assolutamente corretto. Spero che Asker sia in grado di adattarlo per i suoi scopi
Andrey Atapin,

3
[ "$(ls -A /)" ] && touch /non-empty || echo "Empty"- se vuoi "contrassegnare" le directory non vuote con un file creato chiamato non-empty, fallirà e stampa "Vuoto".
uzsolt,

4
dov'è touch /emptynella mia linea?
Andrey Atapin,

7
Oh no! C'è un problema molto importante con questo codice. Dovrebbe essere if [ -n "$(ls -A /path/to/dir)" ]; then... Per favore aggiorna la risposta prima che qualcuno incolli questo codice nel suo server da qualche parte e un hacker capisca come sfruttarlo. Se /path/to/dirnon è vuoto, i nomi dei file lì vengono passati come argomenti a /bin/testcui chiaramente non è previsto. Basta aggiungere l' -nargomento, problema risolto. Grazie!
Edward Ned Harvey il

21

Non c'è bisogno di contare nulla o globs shell. È inoltre possibile utilizzare readin combinazione con find. Se findl'output è vuoto, restituirai false:

if find /some/dir -mindepth 1 | read; then
   echo "dir not empty"
else
   echo "dir empty"
fi

Questo dovrebbe essere portatile.


Bella soluzione, ma penso che le tue echochiamate riflettano il risultato sbagliato: nel mio test (sotto Cygwin) find . -mindepth 1 | readavevo un codice di errore 141 in una directory non vuota e 0 in una directory vuota
Lucas Cimon

@LucasCimon Non qui (macOS e GNU / Linux). Per una directory non vuota, readrestituisce 0 e per una vuota, 1.
slhck

1
PSA non funziona conset -o pipefail
Colonnello Thirty Two

19
if [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; then
    echo "Empty directory"
else
    echo "Not empty or NOT a directory"
fi

Corretto e veloce. Bello!
l0b0

4
Ha bisogno di virgolette (2x) e il test -ndeve essere corretto e sicuro (test con directory con spazi nel nome, test con directory non vuota con nome '0 = 1'). ... [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; ...
Zrin

1
@ivan_pozdeev Questo non è vero, almeno per GNU find. Potresti pensare grep. serverfault.com/questions/225798/…
Vladimir Panteleev il

Potrebbe essere più semplice scrivere find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty | grep .e fare affidamento sullo stato di uscita da grep. Qualunque sia il modo in cui lo fai, questa è decisamente la risposta giusta a questa domanda.
Tom Anderson,

13
#!/bin/bash
if [ -d /path/to/dir ]; then
    # the directory exists
    [ "$(ls -A /path/to/dir)" ] && echo "Not Empty" || echo "Empty"
else
    # You could check here if /path/to/dir is a file with [ -f /path/to/dir]
fi

4
Deve essere così, non è necessario analizzare l'output di ls, basta vedere se è vuoto o no. Usare find mi sembra davvero eccessivo.
Akostadinov,

4

Questo farà il lavoro nella directory di lavoro corrente (.):

[ `ls -1A . | wc -l` -eq 0 ] && echo "Current dir is empty." || echo "Current dir has files (or hidden files) in it."

o lo stesso comando diviso su tre righe solo per essere più leggibile:

[ `ls -1A . | wc -l` -eq 0 ] && \
echo "Current dir is empty." || \
echo "Current dir has files (or hidden files) in it."

Sostituisci semplicemente ls -1A . | wc -lcon ls -1A <target-directory> | wc -lse devi eseguirlo su una cartella di destinazione diversa.

Modifica : ho sostituito -1acon -1A(vedi commento @Daniel)


2
usa ls -Ainvece. Alcuni file system non hanno .e ..collegamenti simbolici secondo i documenti.
Daniel Beck

1
Grazie @ Daniele, ho modificato la mia risposta dopo il tuo suggerimento. So che anche "1" potrebbe essere rimosso.
ztank1013,

2
Non fa male, ma è implicito se non viene inviato a un terminale. Dato che lo installi a un altro programma, è ridondante.
Daniel Beck

-1è decisamente ridondante. Anche se lsnon stampa un articolo per riga quando verrà convogliato, ciò non influisce sull'idea di verificare se ha prodotto zero o più righe.
Victor Yarema,

3

Utilizza il seguente:

count="$( find /path -mindepth 1 -maxdepth 1 | wc -l )"
if [ $count -eq 0 ] ; then
   echo "No new file"
   exit 1
fi

In questo modo, sei indipendente dal formato di output di ls. -mindepthsalta la directory stessa, -maxdepthimpedisce la difesa ricorsiva in sottodirectory per accelerare le cose.


Naturalmente, ora siete dipendenti da wc -le findformato di output (che è ragionevolmente semplice però).
Daniel Beck

3

Utilizzando un array:

files=( * .* )
if (( ${#files[@]} == 2 )); then
    # contents of files array is (. ..)
    echo dir is empty
fi

3
Soluzione molto bella, ma nota che richiedeshopt -s nullglob
xebeche

3
Il ${#files[@]} == 2presupposto non rappresenta la directory principale (probabilmente non si verificherà se è vuoto ma potrebbe essere presente del codice che non conosce tale limitazione).
ivan_pozdeev,

3

Un modo bizzarro, ma solo bash, senza PID:

is_empty() {
    test -e "$1/"* 2>/dev/null
    case $? in
        1)   return 0 ;;
        *)   return 1 ;;
    esac
}

Questo sfrutta il fatto che testbuiltin esce con 2 se viene dato più di un argomento dopo -e: Primo, "$1"/*glob viene espanso di bash. Ciò si traduce in un argomento per file. Così

  • Se non ci sono file, l'asterisco in test -e "$1"*non si espande, quindi Shell torna a provare il file denominato *, che restituisce 1.

  • ... tranne se in realtà esiste un file chiamato esattamente *, allora l'asterisco si espande bene, l'asterisco, che finisce con la stessa chiamata di cui sopra, cioè. test -e "dir/*", proprio questa volta restituisce 0. (Grazie @TrueY per averlo segnalato.)

  • Se è presente un file, test -e "dir/file"viene eseguito, che restituisce 0.

  • Ma se ci sono più file di 1, test -e "dir/file1" "dir/file2" viene eseguito, che bash lo segnala come errore di utilizzo, cioè 2.

case avvolge l'intera logica in modo che solo il primo caso, con 1 stato di uscita, venga segnalato come riuscito.

Possibili problemi che non ho verificato:

  • Ci sono più file che numero di argomenti consentiti - immagino che questo potrebbe comportarsi in modo simile al caso con 2+ file.

  • O in realtà c'è un file con un nome vuoto - non sono sicuro che sia possibile su qualsiasi sistema operativo / FS sano.


1
Correzione minore: se non è presente alcun file in dir /, test -e dir/*viene chiamato. Se l'unico file è '*' in dir, il test restituirà 0. Se ci sono più file, allora restituisce 2. Quindi funziona come descritto.
TrueY

Hai ragione, @ True, l'ho incorporato nella risposta. Grazie!
Alois Mahdal,

2

Con FIND (1) (sotto Linux e FreeBSD) puoi guardare in modo non ricorsivo una voce della directory tramite "-maxdepth 0" e verificare se è vuota con "-empty". Applicato alla domanda questo dà:

if test -n "$(find ./ -maxdepth 0 -empty)" ; then
    echo "No new file"
    exit 1
fi

Potrebbe non essere portatile al 100%, ma è elegante.
Craig Ringer,

1

Penso che la soluzione migliore sia:

files=$(shopt -s nullglob; shopt -s dotglob; echo /MYPATH/*)
[[ "$files" ]] || echo "dir empty" 

grazie a https://stackoverflow.com/a/91558/520567

Questa è una modifica anonima della mia risposta che potrebbe essere o non essere utile a qualcuno: una leggera alterazione fornisce il numero di file:

files=$(shopt -s nullglob dotglob; s=(MYPATH/*); echo ${s[*]}) 
echo "MYPATH contains $files files"

Funzionerà correttamente anche se i nomi dei file contengono spazi.


1
if find "${DIR}" -prune ! -empty -exit 1; then
    echo Empty
else
    echo Not Empty
fi

EDIT: Penso che questa soluzione funzioni bene con gnu find, dopo una rapida occhiata all'implementazione . Ma questo potrebbe non funzionare con, ad esempio, la ricerca di netbsd . In effetti, quello usa il campo st_size di stat (2). Il manuale lo descrive come:

st_size            The size of the file in bytes.  The meaning of the size
                   reported for a directory is file system dependent.
                   Some file systems (e.g. FFS) return the total size used
                   for the directory metadata, possibly including free
                   slots; others (notably ZFS) return the number of
                   entries in the directory.  Some may also return other
                   things or always report zero.

Una soluzione migliore, anche più semplice, è:

if find "${DIR}" -mindepth 1 -exit 1; then
    echo Empty
else
    echo Not Empty
fi

Inoltre, la -pruna nella prima soluzione è inutile.

EDIT: no -exit for gnu find .. la soluzione sopra è buona per la ricerca di NetBSD. Per GNU find, questo dovrebbe funzionare:

if [ -z "`find \"${DIR}\" -mindepth 1 -exec echo notempty \; -quit`" ]; then
    echo Empty
else
    echo Not Empty
fi

find da GNU findutils 4.6.0 (l'ultima versione) non ha un -exitpredicato .
Dennis,

0

Sono tutte cose fantastiche: l'ho appena trasformato in uno script in modo da poter controllare se ci sono directory vuote sotto quella corrente. Il seguente dovrebbe essere messo in un file chiamato 'findempty', posizionato nel percorso da qualche parte in modo che bash possa trovarlo e quindi eseguire 755 per eseguirlo. Immagino che possa essere facilmente modificato in base alle tue esigenze specifiche.

#!/bin/bash
if [ "$#" == "0" ]; then 
find . -maxdepth 1 -type d -exec findempty "{}"  \;
exit
fi

COUNT=`ls -1A "$*" | wc -l`
if [ "$COUNT" == "0" ]; then 
echo "$* : $COUNT"
fi

0

Questo lavoro per me, per controllare ed elaborare i file nella directory ../IN, considerando lo script è nella ../Scriptdirectory:

FileTotalCount=0

    for file in ../IN/*; do
    FileTotalCount=`expr $FileTotalCount + 1`
done

if test "$file" = "../IN/*"
then

    echo "EXITING: NO files available for processing in ../IN directory. "
    exit

else

  echo "Starting Process: Found ""$FileTotalCount"" files in ../IN directory for processing."

# Rest of the Code

0

Che ne dici di verificare se la directory esiste e non vuota in una istruzione if

if [[ -d path/to/dir && -n "$(ls -A path/to/dir)" ]]; then 
  echo "directory exists"
else
  echo "directory doesn't exist"
fi

-1

Per qualsiasi directory diversa da quella corrente, è possibile verificare se è vuota provandoci rmdir, perché rmdirè garantito un errore per le directory non vuote. Se rmdirriesce, e in realtà volevi che la directory vuota sopravvivesse al test, di mkdirnuovo.

Non usare questo trucco se ci sono altri processi che potrebbero essere scomposti da una directory di cui sanno che cessa di esistere brevemente.

Se rmdirnon funziona per te e potresti testare directory che potrebbero potenzialmente contenere un numero elevato di file, qualsiasi soluzione che si basa sul globbing della shell potrebbe rallentare e / o incorrere in limiti di lunghezza della riga di comando. Probabilmente meglio usare findin quel caso. La findsoluzione più veloce che mi viene in mente va come

is_empty() {
    test -z $(find "$1" -mindepth 1 -printf X -quit)
}

Questo funziona per le versioni GNU e BSD di findma non per quella di Solaris, che manca a ciascuno di quegli findoperatori. Adoro il tuo lavoro, Oracle.


Non è una buona idea. L'OP voleva semplicemente verificare se la directory era vuota o meno.
roaima,

-3

Puoi provare a rimuovere la directory e attendere un errore; rmdir non cancellerà la directory se non è vuota.

_path="some/path"
if rmdir $_path >/dev/null 2>&1; then
   mkdir $_path        # create it again
   echo "Empty"
else
   echo "Not empty or doesn't exist"
fi

3
-1 Questo è il tipo di codice che si ritorce contro. rmdirfallirà se non ho i permessi per rimuovere la directory; o se è un sottovolume di Btrfs; o se appartiene a un filesystem di sola lettura. E se rmdirnon fallisce e mkdirviene eseguito: cosa succede se la directory già rimossa appartiene a un altro utente? che dire delle sue autorizzazioni (possibilmente non standard)? ACL? attributi estesi? Tutti persi.
Kamil Maciorowski l'

Bene, sto solo imparando bash e ho pensato che potrebbe essere un modo più veloce piuttosto che iterare attraverso tutta la directory, ma le CPU sono potenti e hai ragione, non sono sicure.
impxd
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.