Comprobando desde el script del shell si un directorio contiene archivos

Desde un script de shell, ¿cómo puedo verificar si un directorio contiene archivos?

Algo similar a esto

if [ -e /some/dir/* ]; then echo "huzzah"; fi; 

pero que funciona si el directorio contiene uno o varios archivos (el anterior solo funciona con exactamente 0 o 1 archivos).

Las soluciones hasta ahora usan ls . Aquí hay una solución completa:

 #!/bin/bash shopt -s nullglob dotglob # To include hidden files files=(/some/dir/*) if [ ${#files[@]} -gt 0 ]; then echo "huzzah"; fi 

Tres mejores trucos


shopt -s nullglob dotglob; f=your/dir/*; ((${#f}))

Este truco es 100% bash e invoca (genera) un subconjunto. La idea es de Bruno De Fraine y mejoró con el comentario de teambob .

 files=$(shopt -s nullglob dotglob; echo your/dir/*) if (( ${#files} )) then echo "contains files" else echo "empty (or does not exist or is a file)" fi 

Nota: no hay diferencia entre un directorio vacío y uno no existente (e incluso cuando la ruta proporcionada es un archivo).

Hay una alternativa similar y más detalles (y más ejemplos) sobre las preguntas frecuentes ‘oficiales’ para el canal IRC #bash :

 if (shopt -s nullglob dotglob; f=(*); ((${#f[@]}))) then echo "contains files" else echo "empty (or does not exist, or is a file)" fi 

[ -n "$(ls -A your/dir)" ]

Este truco está inspirado en el artículo de nixCraft publicado en 2007. Agregue 2>/dev/null para suprimir el error de salida "No such file or directory" .
Ver también la respuesta de Andrew Taylor (2008) y la respuesta de gr8can8dian (2011).

 if [ -n "$(ls -A your/dir 2>/dev/null)" ] then echo "contains files (or is a file)" else echo "empty (or does not exist)" fi 

o la versión de bashism de una línea:

 [[ $(ls -A your/dir) ]] && echo "contains files" || echo "empty" 

Nota: ls devuelve $?=2 cuando el directorio no existe. Pero no hay diferencia entre un archivo y un directorio vacío.


[ -n "$(find your/dir -prune -empty)" ]

Este último truco está inspirado en la respuesta de -maxdepth 0 donde -maxdepth 0 es reemplazado por -prune y mejorado por el comentario de -prune .

 if [ -n "$(find your/dir -prune -empty 2>/dev/null)" ] then echo "empty (directory or file)" else echo "contains files (or does not exist)" fi 

una variación usando -type d :

 if [ -n "$(find your/dir -prune -empty -type d 2>/dev/null)" ] then echo "empty directory" else echo "contains files (or does not exist or is not a directory)" fi 

Explicación:

  • find -prune es similar a find -maxdepth 0 usando menos caracteres
  • find -empty imprime los directorios y archivos vacíos
  • find -type d imprime solo directorios

Nota: También puede reemplazar [ -n "$(find your/dir -prune -empty)" ] por la versión shorten a continuación:

 if [ `find your/dir -prune -empty 2>/dev/null` ] then echo "empty (directory or file)" else echo "contains files (or does not exist)" fi 

Este último código funciona en la mayoría de los casos, pero tenga en cuenta que las rutas maliciosas podrían express un comando …

¿Qué tal lo siguiente?

 if find /some/dir/ -maxdepth 0 -empty | read v; then echo "Empty dir"; fi 

De esta forma, no es necesario generar una lista completa de los contenidos del directorio. La read es tanto para descartar el resultado como para hacer que la expresión se evalúe como verdadera solo cuando algo se lee (es decir, /some/dir/ se encuentra vacío por find ).

Tratar:

 if [ ! -z `ls /some/dir/*` ]; then echo "huzzah"; fi 
 # Works on hidden files, directories and regular files ### isEmpty() # This function takes one parameter: # $1 is the directory to check # Echoes "huzzah" if the directory has files function isEmpty(){ if [ "$(ls -A $1)" ]; then echo "huzzah" else echo "has no files" fi } 

¡Cuida los directorios con muchos archivos! Podría tomar algún tiempo evaluar el comando ls .

IMO la mejor solución es la que usa

 find /some/dir/ -maxdepth 0 -empty 
 DIR="/some/dir" if [ "$(ls -A $DIR)" ]; then echo 'There is something alive in here' fi 

¿Podría comparar el resultado de esto?

  ls -A /some/dir | wc -l 
 # Comprueba si un directorio contiene algún archivo no oculto.
 #
 # uso: if isempty "$ HOME";  luego repite "Bienvenido a casa";  fi
 #
 esta vacio() {
     para _ief en $ 1 / *;  hacer
         si [-e "$ _ief"];  entonces
             regreso 1
         fi
     hecho
     return 0
 }

Algunas notas de implementación:

  • El bucle for evita una llamada a un proceso ls externo. Todavía lee todas las entradas del directorio una vez. Esto solo se puede optimizar escribiendo un progtwig en C que use readdir () explícitamente.
  • La test -e dentro del ciclo capta el caso de un directorio vacío, en cuyo caso a la variable _ief se le asignará el valor “somedir / *”. Solo si ese archivo existe la función devolverá “no vacío”
  • Esta función funcionará en todas las implementaciones de POSIX. Pero tenga en cuenta que Solaris / bin / sh no entra en esa categoría. Su implementación de test no admite el indicador -e .

Esta puede ser una respuesta muy tardía, pero aquí hay una solución que funciona. ¡Esta línea solo reconoce la existencia de archivos! No le dará un falso positivo si existen directorios.

 if find /path/to/check/* -maxdepth 0 -type f | read then echo "Files Exist" fi 

Esto me dice si el directorio está vacío o si no lo está, la cantidad de archivos que contiene.

 directory="/some/dir" number_of_files=$(ls -A $directory | wc -l) if [ "$number_of_files" == "0" ]; then echo "directory $directory is empty" else echo "directory $directory contains $number_of_files files" fi 
 dir_is_empty() { [ "${1##*/}" = "*" ] } if dir_is_empty /some/dir/* ; then echo "huzzah" fi 

Supongamos que no tiene un archivo llamado * en /any/dir/you/check , debería funcionar en bash dash posh busybox sh y zsh pero (para zsh) requiere unsetopt nomatch .

Las interpretaciones deben ser comparables a cualquier ls que use * (glob), supongo que será lento en directorios con muchos nodos (mi /usr/bin con 3000+ archivos no fue tan lento), usará al menos memoria suficiente para asignar todos los directorios / nombres de archivo (y más), ya que todos ellos son pasados ​​(resueltos) a la función como argumentos, algunos shell probablemente tengan límites en el número de argumentos y / o la longitud de los argumentos.

Una manera portátil de O (1) cero recursos para verificar si un directorio está vacío sería bueno tener.

actualizar

La versión anterior no tiene en cuenta los archivos / directorios ocultos, en caso de que se requiera más prueba, como is_empty de los trucos de Rich’s sh (POSIX shell) :

 is_empty () ( cd "$1" set -- .[!.]* ; test -f "$1" && return 1 set -- ..?* ; test -f "$1" && return 1 set -- * ; test -f "$1" && return 1 return 0 ) 

Pero, en cambio, estoy pensando en algo como esto:

 dir_is_empty() { [ "$(find "$1" -name "?*" | dd bs=$((${#1}+3)) count=1 2>/dev/null)" = "$1" ] } 

Algunos se preocupan por las diferencias al final del argumento y la salida de búsqueda cuando el directorio está vacío, y las nuevas líneas finales (pero esto debería ser fácil de manejar), lamentablemente en mi busybox sh mostrar lo que probablemente sea un error en el find -> dd pipe con la salida truncada aleatoriamente (si utilicé cat la salida es siempre la misma, parece ser dd con el count argumentos).

Pequeña variación de la respuesta de Bruno :

 files=$(ls -1 /some/dir| wc -l) if [ $files -gt 0 ] then echo "Contains files" else echo "Empty" fi 

Esto funciona para mi

ZSH

Sé que la pregunta fue marcada para bash; pero, solo como referencia, para usuarios de zsh :

Prueba para el directorio no vacío

Para verificar si foo no está vacío:

 $ for i in foo(NF) ; do ... ; done 

donde, si foo no está vacío, se ejecutará el código en el bloque for .

Prueba de directorio vacío

Para verificar si foo está vacío:

 $ for i in foo(N/^F) ; do ... ; done 

donde, si foo está vacío, se ejecutará el código en el bloque for .

Notas

No necesitamos citar el directorio foo anterior, pero podemos hacerlo si tenemos que:

 $ for i in 'some directory!'(NF) ; do ... ; done 

También podemos probar más de un objeto, incluso si no es un directorio:

 $ mkdir X # empty directory $ touch f # regular file $ for i in X(N/^F) f(N/^F) ; do echo $i ; done # echo empty directories X 

Todo lo que no sea un directorio será ignorado.

Extras

Como estamos englobando, podemos usar cualquier glob (o expansión de corsé):

 $ mkdir X X1 X2 Y Y1 Y2 Z $ touch Xf # create regular file $ touch X1/f # directory X1 is not empty $ touch Y1/.f # directory Y1 is not empty $ ls -F # list all objects X/ X1/ X2/ Xf Y/ Y1/ Y2/ Z/ $ for i in {X,Y}*(N/^F); do printf "$i "; done; echo # print empty directories X X2 Y Y2 

También podemos examinar objetos que se colocan en una matriz. Con los directorios como arriba, por ejemplo:

 $ ls -F # list all objects X/ X1/ X2/ Xf Y/ Y1/ Y2/ Z/ $ arr=(*) # place objects into array "arr" $ for i in ${^arr}(N/^F); do printf "$i "; done; echo X X2 Y Y2 Z 

Por lo tanto, podemos probar objetos que ya se pueden establecer en un parámetro de matriz.

Tenga en cuenta que el código en el bloque for se ejecuta, obviamente, en cada directorio por turno. Si esto no es deseable, puede completar un parámetro de matriz y luego operar en ese parámetro:

 $ for i in *(NF) ; do full_directories+=($i) ; done $ do_something $full_directories 

Explicación

Para los usuarios de zsh existe el calificador glob (F) (ver man zshexpn ), que coincide con directorios “completos” (no vacíos):

 $ mkdir XY $ touch Y/.f # Y is now not empty $ touch f # create a regular file $ ls -dF * # list everything in the current directory f X/ Y/ $ ls -dF *(F) # will list only "full" directories Y/ 

El calificador (F) enumera los objetos que coinciden: es un directorio Y no está vacío. Entonces, (^F) coincide: no es un directorio O está vacío. Por lo tanto, (^F) solo también incluiría archivos regulares, por ejemplo. Por lo tanto, como se explica en la página man de zshexp , también necesitamos el calificador (/) glob, que enumera solo los directorios:

 $ mkdir XYZ $ touch X/f Y/.f # directories X and Y now not empty $ for i in *(/^F) ; do echo $i ; done Z 

Por lo tanto, para verificar si un directorio determinado está vacío, puede ejecutar:

 $ mkdir X $ for i in X(/^F) ; do echo $i ; done ; echo "finished" X finished 

y solo para asegurarse de que un directorio no vacío no sea capturado:

 $ mkdir Y $ touch Y/.f $ for i in Y(/^F) ; do echo $i ; done ; echo "finished" zsh: no matches found: Y(/^F) finished 

Oops! Como Y no está vacío, zsh no encuentra coincidencias para (/^F) (“directorios que están vacíos”) y, por lo tanto, escupe un mensaje de error que indica que no se encontraron coincidencias para el glob. Por lo tanto, necesitamos suprimir estos posibles mensajes de error con el calificador (N) glob:

 $ mkdir Y $ touch Y/.f $ for i in Y(N/^F) ; do echo $i ; done ; echo "finished" finished 

Por lo tanto, para directorios no vacíos, necesitamos el calificador (N/^F) , que puede leer como: “no me avise sobre fallas, directorios que no estén completos”.

Del mismo modo, para los directorios vacíos, necesitamos el calificador (NF) , que también podemos leer como: “no me avisen sobre errores, directorios completos”.

 if ls /some/dir/* >/dev/null 2>&1 ; then echo "huzzah"; fi; 

Me sorprende que la guía de wooledge en directorios vacíos no haya sido mencionada. Esta guía, y realmente todo, es una lectura obligada para preguntas tipo shell.

De la nota de esa página:

Nunca intente analizar la salida de ls. Incluso ls soluciones pueden romperse (por ejemplo, en HP-UX, si eres root, ls -A hace exactamente lo contrario de lo que hace si no eres root) y no, no puedo inventar algo increíblemente estúpido).

De hecho, uno puede desear evitar la pregunta directa por completo. Por lo general, la gente quiere saber si un directorio está vacío porque quiere hacer algo relacionado con los archivos que contiene, etc. Consulte la pregunta más amplia. Por ejemplo, uno de estos ejemplos basados ​​en resultados puede ser una solución adecuada:

  # Bourne find "$somedir" -type f -exec echo Found unexpected file {} \; find "$somedir" -maxdepth 0 -empty -exec echo {} is empty. \; # GNU/BSD find "$somedir" -type d -empty -exec cp /my/configfile {} \; # GNU/BSD 

Más comúnmente, todo lo que realmente se necesita es algo como esto:

  # Bourne for f in ./*.mpg; do test -f "$f" || continue mympgviewer "$f" done 

En otras palabras, la persona que hace la pregunta puede haber pensado que era necesaria una prueba explícita de directorio vacío para evitar un mensaje de error como mympgviewer: ./*.mpg: No existe tal archivo o directorio cuando en realidad no se requiere tal prueba.

Hasta ahora no he visto una respuesta que use grep, que creo que daría una respuesta más simple (¡sin demasiados símbolos extraños!). Aquí es cómo verificaría si existen archivos en el directorio usando bourne shell:

esto devuelve la cantidad de archivos en un directorio:

 ls -l  | egrep -c "^-" 

puede completar la ruta del directorio donde está escrito el directorio. La primera mitad de la tubería asegura que el primer carácter de salida es “-” para cada archivo. egrep luego cuenta el número de líneas que comienzan con ese símbolo usando expresiones regulares. ahora todo lo que tiene que hacer es almacenar el número que obtiene y compararlo usando comillas inversas como:

  #!/bin/sh fileNum=`ls -l  | egrep -c "^-"` if [ $fileNum == x ] then #do what you want to do fi 

x es una variable de tu elección.

Mezclando podas y últimas respuestas, llegué a

 find "$some_dir" -prune -empty -type d | read && echo empty || echo "not empty" 

eso funciona para caminos con espacios también

Respuesta simple con bash :

 if [[ $(ls /some/dir/) ]]; then echo "huzzah"; fi; 

Yo buscaría:

 if [ -z "$(find $dir -maxdepth 1 -type f)" ]; then echo "$dir has NO files" else echo "$dir has files" 

Esto verifica el resultado de buscar solo archivos en el directorio, sin pasar por los subdirectorios. Luego verifica la salida usando la opción -z tomada de la man test :

  -z STRING the length of STRING is zero 

Vea algunos resultados:

 $ mkdir aaa $ dir="aaa" 

Dir vacío:

 $ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty" empty 

Solo dirs en él:

 $ mkdir aaa/bbb $ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty" empty 

Un archivo en el directorio:

 $ touch aaa/myfile $ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty" $ rm aaa/myfile 

Un archivo en un subdirectorio:

 $ touch aaa/bbb/another_file $ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty" empty 

Prueba con el comando find. Especifique el directorio codificado o como argumento. Luego inicie find para buscar todos los archivos dentro del directorio. Compruebe si la devolución de find es nula. Echo los datos del hallazgo

 #!/bin/bash _DIR="/home/user/test/" #_DIR=$1 _FIND=$(find $_DIR -type f ) if [ -n "$_FIND" ] then echo -e "$_DIR contains files or subdirs with files \n\n " echo "$_FIND" else echo "empty (or does not exist)" fi 

Con alguna solución, podría encontrar una manera simple de averiguar si hay archivos en un directorio. Esto puede extenderse más con los comandos grep para verificar específicamente archivos .xml o .txt, etc. Ej .: ls /some/dir | grep xml | wc -l | grep -w "0" ls /some/dir | grep xml | wc -l | grep -w "0"

 #!/bin/bash if ([ $(ls /some/dir | wc -l | grep -w "0") ]) then echo 'No files' else echo 'Found files' fi 

No me gusta el ls - A solución publicada. Lo más probable es que desee probar si el directorio está vacío porque no desea eliminarlo. Lo siguiente hace eso. Sin embargo, si solo desea registrar un archivo vacío, seguramente eliminarlo y volver a crearlo es más rápido que enumerar archivos posiblemente infinitos.

Esto debería funcionar…

 if ! rmdir ${target} then echo "not empty" else echo "empty" mkdir ${target} fi 

para probar un directorio objective específico

 if [ -d $target_dir ]; then ls_contents=$(ls -1 $target_dir | xargs); if [ ! -z "$ls_contents" -a "$ls_contents" != "" ]; then echo "is not empty"; else echo "is empty"; fi; else echo "directory does not exist"; fi; 

Funciona bien para mí esto (cuando existe directorio):

 some_dir="/some/dir with whitespace & other characters/" if find "`echo "$some_dir"`" -maxdepth 0 -empty | read v; then echo "Empty dir"; fi 

Con un cheque completo:

 if [ -d "$some_dir" ]; then if find "`echo "$some_dir"`" -maxdepth 0 -empty | read v; then echo "Empty dir"; else "Dir is NOT empty" fi fi