Tengo lo siguiente en un script de shell:
for file in $local_dir/myfile.log.*;
do
file_name=$(basename $file);
server_name=$(echo $file_name | cut -f 3 -d '.');
file_location=$(echo $file);
mv $file_location $local_dir/in_progress1.log
mysql -hxxx -P3306 -uxxx -pxxx -e "set @server_name='${server_name}'; source ${sql_script};"
rm $local_dir/in_progress1.log
done
Básicamente, obtiene todos los archivos en un directorio que coinciden con los criterios, extrae un nombre de servidor del nombre del archivo antes de pasarlo a un script MySQL para su procesamiento.
Lo que me pregunto es si tengo 10 archivos que tardan 60 segundos cada uno en completarse y, después de 5 minutos, inicio una segunda instancia del script de shell:
- a) ¿El segundo script seguirá viendo los archivos que no se han procesado?
- b) ¿causará problemas en primera instancia si elimina archivos?
¿O podré ejecutarlos en paralelo sin problemas?
Respuesta1
Se podría suponer que "60 segundos" (e incluso "5 minutos") es sólo una buena estimación, y que existe el riesgo de que el primer lote todavía esté en progreso cuando se inicie el segundo lote. Si desea separar los lotes (y si no hay ningún problema aparte de los archivos de registro que se superponen ocasionalmente), un mejor enfoque sería crear un número de lote como parte de la convención de nombres de archivos en curso.
Algo como esto:
[[ -s ]] $local_dir/batch || echo 0 > $local_dir/batch
batch=$(echo $local_dir/batch)
expr $batch + 1 >$local_dir/batch
antes del bucle for y luego al inicio del bucle, verifique que su patrón coincida con un archivo real
[[ -f "$file" ]] || continue
y use el número de lote en el nombre del archivo:
mv $file_location $local_dir/in_progress$batch.log
y para adelante. Eso reduce el riesgo de colisión.
Respuesta2
Hay una respuesta anterior que proporciona algunas buenas soluciones al problema, pero pensé en dar una pequeña explicación sobre elpor quéde cuál es el problema.
En su mayor parte: siempre que sus archivos de registro renombrados (los que están en progreso) no cumplan con los criterios, noprobablementeseguro ejecutar esto conmínimoriesgo. Aunque seguirás recibiendo algunos errores...
Su lista de archivos se genera al ejecutar el script. Entonces lo que terminaría pasando es que:
Script A
obtiene una lista de 10 files
. Comienza el procesamiento, 5 files
en (5 restantes) script B
obtiene una lista de 5 remaining files
, comienza el procesamiento. Script a
luego va a procesar el siguiente archivo en su lista (que es el mismo que el archivo script B
que comenzó a procesar). Se producirá un error porque se cambió el nombre del archivo. Entonces, con el manejo de errores, teóricamente podría pasar al siguiente en su lista y funcionar sin problemas. Pero, obviamente, SIEMPRE existe la posibilidad de que las estrellas se alineen, pero los scripts llegan al mismo archivo al mismo tiempo y sucede algo inesperado. Sopesa ese riesgo como quieras.
Una solución potencialmente más elegante sería convertir esto en un python
script y analizar parallel for loops
cuál le permitiría crear un único bucle for y ejecutarlo en paralelo, permitiendo que un script haga el trabajo de dos o más.
Respuesta3
Otra forma de hacerlo es implementar una cola por lotes simple en su script.
Al comienzo del script, podrías hacer algo como esto:
mkdir -p $localdir/batch
BATCHTMP=$(mktemp batch.XXXXXXXXXX)
MYBATCH="$localdir/batch/batch.$$"
# get list of current log files
find $local_dir/ -name 'myfile.log.*' > "$BATCHTMP"
# exclude any log files already in other batches
grep -vF -f <(sort -u $localdir/batch/batch.*) < "$BATCHTMP" > "$MYBATCH"
rm -f "$BATCHTMP"
# only process log files that are in my batch
for lf in $(cat "$MYBATCH") ; do
....
# somewhere in here, mv or rm the logfile being processed
# so it doesn't get processed again in a later batch run
done
rm -f "$MYBATCH"
Por supuesto, esto es sólo un simple resumen de lo que hay que hacer.
Por cierto, esto también se podría hacer en un script contenedor que no hace más que generar el archivo por lotes y luego ejecutar el script principal.