Estoy intentando escribir un script bash en el que intento encontrar si alguna ubicación tiene archivos vacíos o no y enviar un correo electrónico si los encuentro. Primero pensé en combinar "buscar" y "correo", pero si la ubicación tiene varios archivos vacíos, enviaría varios correos electrónicos, lo cual no quiero, así que pensé en establecer una variable de marca en cero antes de buscarla y configurarla. a 1 dentro de buscar si se encuentra un archivo vacío. Esto es lo que probé:
FLAG=0
find $LOCATION -size 0 -type f -exec sh -c 'export FLAG=1' \;
echo $FLAG
pero el problema es que, incluso si la ubicación tiene archivos vacíos, el valor del indicador no cambia a 1. ¿Qué estoy haciendo mal?
Respuesta1
set -- "${LOCATION}/"*
while [ -s "$1" ] ; do shift ; done
[ -e "$1" ] && FLAG=1
El shell integrado [ test ]
se puede utilizar con el -s
operador ize. De man test
:
-s FILE
FILE
existe y tiene un tamaño mayor que cero
Sin embargo, eso no funcionará fácilmente para búsquedas recursivas.
find
lo hará, pero para una cuestión tan simple como establecer un valor booleano, realmente no es una buena opción para este caso. ls
puede realizar búsquedas recursivas con la misma rapidez, se puede configurar fácilmente para proporcionar el tamaño del archivo como primer campo de un listado y para enumerar solo una entrada por línea, garantizado. Si desea establecer el valor booleano de una variable de shell en función de si una lista de archivos recursiva contiene o no un archivo de tamaño 0, entonces esta ls
es su mejor opción: find
solo complica las cosas. Lo que le interesa son las propiedades de los archivos, no las ubicaciones de los archivos. Aquí es donde ls
brilla, y grep
hacer ping a su resultado es muy sencillo.
Puedes hacerlo así de fácil:
ls -1aqRsp "$LOCATION" 2>&1 | grep -qv "^ *[^0]\|/"
FLAG=$(($?==0))
Eso solo se configurará FLAG
si 1
un archivo...oculto .dot
archivos incluidos: existe en $LOCATION
o en uno de sus directorios secundarios con un tamaño de cero. De lo contrario, $FLAG
es 0
.
Respuesta2
find
está bifurcando un nuevo proceso hijo cuando lo hace -exec
. Ningún proceso hijo puede alterar el entorno de su padre.
Podrías considerar recopilar los nombres de los archivos en cuestión find
y luego generar el correo electrónico que deseas en una segunda pasada:
find . -type f -size 0 -print >> /var/tmp/find.sz0
...
Respuesta3
Esto export FLAG=1
se realiza en los casos de sh
invocación por find
. El entorno de un proceso nunca se copia a su padre. Un proceso de shell que solo establece una variable de entorno y sale no logra nada duradero.
Si desea probar si find
coincide con algún archivo, simplemente puede probar si su salida está vacía. Para evitar potencialmente generar una larga lista de archivos solo para descartarla una vez que se haya demostrado que no está vacía, puede truncar la find
salida en la primera oportunidad. GNU find tiene un -quit
predicado y también puede indicarle fácilmente que muestre un solo carácter. En el siguiente fragmento, FLAG
termina vacío si no hay ninguna coincidencia:
FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)
La siguiente variante se establece FLAG
en 0 o 1:
FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)
[ -n "$FLAG" ] || FLAG=0
Si desea seguir siendo portátil, puede realizar find
coincidencias de impresión y conservar sólo la primera línea; find
morirá de unSIGPIPEdespués head
cierra su lado de la tubería.
if [ -n "$(find "$LOCATION" -size 0 -type f -print | head -n 1 \;)" ]; then
FLAG=1
else
FLAG=0
fi
Si este fuera solo un ejemplo simplificado y necesita establecer variables más complejas, existen varios enfoques posibles. Sólo mencionaré un par de los más comunes.
Zsh tiene muchos casos de uso find
integrados gracias aclasificatorios globales. El siguiente fragmento se establece files
en la lista de archivos que coinciden con find $LOCATION -size 0 -type f
:
files=(**/*(.DL0))
Ksh y bash también tienen globos recursivos si se habilitan con set -o globstar
y shopt -s globstar
respectivamente. Sin embargo, tenga en cuenta que con bash hasta 4.2, **
se recurre a enlaces simbólicos a directorios, lo que normalmente no es deseable. Puede realizar más procesamiento en el shell.
FIGNORE= # include dot files in wildcard matches (the ksh way)
GLOBIGNORE=.:.. # include dot files in wildcard matches (the bash way)
for x in **/*; do
done
Si ninguno de los nombres de archivos coincidentes contiene nuevas líneas, puede analizar la salida find
como una lista delimitada por nuevas líneas.
find "$LOCATION" -size 0 -type f ! -name '*
*' -print | {
while read -r empty_file; do
…
done
}