Bash scripting: prueba de directorio vacío

95

Quiero probar si un directorio no contiene ningún archivo. Si es así, omitiré algún procesamiento.

Intenté lo siguiente:

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

Eso da el siguiente error:

line 1: [: too many arguments

¿Hay una solución / alternativa?

Anthony Kong
fuente
relacionado: stackoverflow.com/q/91368/52074
Trevor Boyd Smith

Respuestas:

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

Además, sería genial verificar si el directorio existe antes.

Andrey Atapin
fuente
11
¡No lo uses &&y al ||mismo tiempo! Si echo "Not Empty"falla, echo "Empty"correrá! Trate echo "test" && false || echo "fail"! Sí, sé echoque no fallará, pero si cambia cualquier otro comando, ¡se sorprenderá!
uzsolt
44
Proporcione al menos un ejemplo cuando el código anterior no funcione. Concretamente, este código es absolutamente correcto. Espero que autor de la pregunta es capaz de adaptar esto para sus propios fines
Andrey Atapin
3
[ "$(ls -A /)" ] && touch /non-empty || echo "Empty"- si desea "marcar" los directorios no vacíos con un archivo creado llamado non-empty, fallará e imprimirá "Vacío".
uzsolt
44
¿Dónde está touch /emptymi línea?
Andrey Atapin
77
¡Oh no! Hay un problema muy importante con este código. Debería ser if [ -n "$(ls -A /path/to/dir)" ]; then... Actualice la respuesta antes de que alguien pegue este código en su servidor en algún lugar y un hacker descubra cómo explotarlo. Si /path/to/dirno está vacío, los nombres de archivo se pasan como argumentos a los /bin/testque claramente no se pretende. Simplemente agregue el -nargumento, problema resuelto. ¡Gracias!
Edward Ned Harvey
21

No es necesario contar nada ni globos de concha. También se puede usar readen combinación con find. Si findla salida está vacía, devolverá false:

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

Esto debería ser portátil.

slhck
fuente
Buena solución, pero creo que sus echollamadas reflejan el resultado incorrecto: en mi prueba (bajo Cygwin) find . -mindepth 1 | readtenía un código de error 141 en un directorio no vacío y 0 en un directorio vacío
Lucas Cimon
@LucasCimon No aquí (macOS y GNU / Linux). Para un directorio no vacío, readdevuelve 0, y para uno vacío, 1.
slhck
1
PSA no funciona conset -o pipefail
Coronel Treinta y Dos
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
uzsolt
fuente
Correcto y rápido. ¡Agradable!
l0b0
44
Necesita comillas (2x) y la prueba -npara ser correcta y segura (prueba con directorio con espacios en el nombre, prueba con directorio no vacío con nombre '0 = 1'). ... [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; ...
Zrin
1
@ivan_pozdeev Eso no es cierto, al menos para GNU find. Puedes estar pensando grep. serverfault.com/questions/225798/…
Vladimir Panteleev
Puede ser más simple escribir find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty | grep .y confiar en el estado de salida de grep. De cualquier forma que lo haga, esta es la respuesta correcta a esta pregunta.
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
Renaud
fuente
44
Eso debe ser, no es necesario analizar la salida de ls, solo ver si está vacía o no. Usar find me parece una exageración.
akostadinov
4

Esto hará el trabajo en el directorio de trabajo actual (.):

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

o el mismo comando dividido en tres líneas solo para ser más legible:

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

Simplemente reemplácelo ls -1A . | wc -lcon ls -1A <target-directory> | wc -lsi necesita ejecutarlo en una carpeta de destino diferente.

Editar : lo reemplacé -1acon -1A(vea el comentario de @Daniel)

ztank1013
fuente
2
utilizar ls -Aen su lugar. Algunos sistemas de archivos no tienen .y ..los enlaces simbólicos de acuerdo a la documentación.
Daniel Beck
1
Gracias @Daniel, edité mi respuesta después de su sugerencia. Sé que el "1" también podría eliminarse.
ztank1013 31/10/11
2
No duele, pero está implícito si no se envía a un terminal. Como lo canaliza a otro programa, es redundante.
Daniel Beck
-1Es definitivamente redundante. Incluso si lsno imprime un elemento por línea cuando se canalizará, no afectará la idea de verificar si produjo cero o más líneas.
Victor Yarema
3

Use lo siguiente:

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

De esta manera, eres independiente del formato de salida de ls. -mindepthomite el directorio en sí, -maxdepthevita la defensa recursiva en subdirectorios para acelerar las cosas.

Daniel Beck
fuente
Por supuesto, usted es ahora dependiente wc -ly findformato de salida (que es razonablemente sencillo sin embargo).
Daniel Beck
3

Usando una matriz:

files=( * .* )
if (( ${#files[@]} == 2 )); then
    # contents of files array is (. ..)
    echo dir is empty
fi
Glenn Jackman
fuente
3
Muy buena solución, pero tenga en cuenta que requiereshopt -s nullglob
xebeche
3
La ${#files[@]} == 2suposición no representa el directorio raíz (probablemente no probará si está vacío, pero podría haber algún código que no conozca esa limitación).
ivan_pozdeev
3

Una manera hacky, pero bash-only, sin PID:

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

Esto aprovecha el hecho de que el testbuiltin sale con 2 si se le da más de un argumento después -e: Primero, "$1"/*bash expande glob. Esto da como resultado un argumento por archivo. Entonces

  • Si no hay archivos, el asterisco test -e "$1"*no se expande, por lo que Shell recurre al archivo de prueba llamado *, que devuelve 1.

  • ... excepto si realmente hay un archivo llamado exactamente *, entonces el asterisco se expande bien, asterisco, que termina como la misma llamada que antes, es decir. test -e "dir/*", solo que esta vez devuelve 0. (Gracias @TrueY por señalar esto).

  • Si hay un archivo, test -e "dir/file"se ejecuta, que devuelve 0.

  • Pero si hay más archivos que 1, test -e "dir/file1" "dir/file2" se ejecuta, que bash lo informa como un error de uso, es decir, 2.

case envuelve toda la lógica para que solo el primer caso, con 1 estado de salida, sea reportado como exitoso.

Posibles problemas que no he verificado:

  • Hay más archivos que número de argumentos permitidos. Supongo que esto podría comportarse de manera similar al caso con 2+ archivos.

  • O en realidad hay un archivo con un nombre vacío: no estoy seguro de que sea posible en cualquier sistema operativo / FS sano.

Alois Mahdal
fuente
1
Corrección menor: si no hay un archivo en dir /, entonces test -e dir/*se llama. Si el único archivo es '*' en el directorio, entonces la prueba devolverá 0. Si hay más archivos, entonces devuelve 2. Entonces funciona como se describe.
Verdaderamente el
Tienes razón, @TrueY, lo he incorporado en la respuesta. ¡Gracias!
Alois Mahdal
2

Con FIND (1) (bajo Linux y FreeBSD) puede mirar de forma no recursiva una entrada de directorio a través de "-maxdepth 0" y probar si está vacía con "-empty". Aplicado a la pregunta esto da:

if test -n "$(find ./ -maxdepth 0 -empty)" ; then
    echo "No new file"
    exit 1
fi
TimJ
fuente
Puede que no sea 100% portátil, pero es elegante.
Craig Ringer el
1

Creo que la mejor solución es:

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

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

Esta es una edición anónima de mi respuesta que puede o no ser útil para alguien: una ligera alteración da la cantidad de archivos:

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

Esto funcionará correctamente incluso si los nombres de archivo contienen espacios.

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

EDITAR: Creo que esta solución funciona bien con gnu find, después de un rápido vistazo a la implementación . Pero esto puede no funcionar con, por ejemplo, el hallazgo de netbsd . De hecho, ese usa el campo st_size de stat (2). El manual lo describe como:

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 mejor solución, también más simple, es:

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

Además, la -prune en la primera solución es inútil.

EDITAR: no -exit para gnu find ... la solución anterior es buena para el hallazgo de NetBSD. Para GNU find, esto debería funcionar:

if [ -z "`find \"${DIR}\" -mindepth 1 -exec echo notempty \; -quit`" ]; then
    echo Empty
else
    echo Not Empty
fi
hilo
fuente
find from GNU findutils 4.6.0 (la última versión) no tiene un -exitpredicado .
Dennis
0

Todo esto es excelente, solo lo convertí en un script para que pueda verificar si hay directorios vacíos debajo del actual. Lo siguiente debe colocarse en un archivo llamado 'findempty', ubicado en la ruta en algún lugar para que bash pueda encontrarlo y luego ejecutar chmod 755. Supongo que puede modificarse fácilmente a sus necesidades específicas.

#!/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
Warren Sherliker
fuente
0

Esto funciona para mí, para verificar y procesar archivos en el directorio ../IN, considerando que el script está en el ../Scriptdirectorio:

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
Arijit
fuente
0

¿Qué pasa con la prueba si el directorio existe y no está vacío en una declaración if?

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

Para cualquier directorio que no sea el actual, puede verificar si está vacío al intentarlo rmdir, porque rmdirse garantiza que fallará en los directorios no vacíos. Si rmdirtiene éxito, y realmente quería que el directorio vacío sobreviviera a la prueba, solo mkdirvuelva a hacerlo.

No utilices este truco si hay otros procesos que podrían descomponerse por un directorio que conocen sobre dejar brevemente de existir.

Si rmdirno funciona para usted, y podría estar probando directorios que podrían contener una gran cantidad de archivos, cualquier solución que se base en el bloqueo de shell podría volverse lenta y / o encontrarse con límites de longitud de línea de comando. Probablemente mejor usar finden ese caso. La findsolución más rápida que se me ocurre va como

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

Esto funciona para las versiones GNU y BSD, findpero no para Solaris, a la que le faltan todos estos findoperadores. Amo tu trabajo, Oracle.

Flabdablet
fuente
No es Buena idea. El OP simplemente quería probar si el directorio estaba vacío o no.
roaima
-3

Puede intentar eliminar el directorio y esperar un error; rmdir no eliminará el directorio si no está vacío.

_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
impxd
fuente
3
-1 Este es el tipo de código que falla. rmdirfallará si no tengo permiso para eliminar el directorio; o si es un subvolumen Btrfs; o si pertenece a un sistema de archivos de solo lectura. Y si rmdirno falla y se mkdirejecuta: ¿qué pasa si el directorio ya eliminado pertenecía a otro usuario? ¿Qué pasa con sus permisos (posiblemente no estándar)? ACL? atributos extendidos? Todo perdido
Kamil Maciorowski
Bueno, solo estoy aprendiendo bash y pensé que podría ser una forma más rápida en lugar de recorrer todo el directorio, pero las CPU son potentes y tienes razón, no son seguras.
impxd