Tengo esta estructura de directorios en /var/sync
:
# tree -d /var/sync/
/var/sync/
├── sync_bi
│ ├── replicator => Replicator.php
│ sync.php
├── sync_pfizer
│ ├── replicator => Replicator.php
│ sync.php
│ replicator.sh
│ sync.sh
Como puede ver, en cada sync_*
directorio hay un script llamado sync.php
y en cada replicator
directorio hay un script llamado Replicator.php
. Necesito ejecutar esos scripts cada noche. Replicator.php
debe ejecutarse a las 00:00 a. m. y sync.php
debe ejecutarse a las 00:30 a. m. Sé que el camino correcto aquí es obviamente un trabajo cron. He creado dos scripts replicator.sh
y sync.sh
comparto casi el mismo código, la principal diferencia es el nombre del script. Ver código a continuación:
replicator.sh
#! /bin/bash
cd $1
/usr/bin/php Replicator.php
sync.sh
#! /bin/bash
cd $1
/usr/bin/php sync.php
Luego agregué cada uno a cronjob de la siguiente manera:
0 0 * * * /var/sync/replicator.sh /var/sync/sync_bi/replicator/
0 30 * * * /var/sync/sync.sh /var/sync/sync_bi/
0 2 * * * /var/sync/replicator.sh /var/sync/sync_pfizer/replicator/
30 2 * * * /var/sync/sync.sh /var/sync/sync_pfizer/
Mi idea detrás de toda esta explicación es optimizar mi entorno cambiando el script a uno que me permita realizar todas las acciones en una sola llamada cronjob y mantener menos archivos que mantener. Así que investigué un poco y encontré temas útiles:
- Usar
find some/dir -type f -execdir somecommand {} \;
es una forma adecuada de lograr lo mismo que estoy haciendo concd
Y moviéndose a cada directorio bajo la ruta raíz, en mi caso
/var/sync
for D in ./*; do if [ -d "$D" ]; then cd "$D" run_something cd .. fi done
Este es un pseudocódigo de lo que estoy hablando (en caso de que no esté lo suficientemente claro):
read tree structure at /var/sync
foreach (directory in root+path)
if (directory_name is replicator)
cd replicator/
/usr/bin/php replicator.php
else if (directory_name is sync_*)
cd sync_*/
/usr/bin/php sync.php
endforeach
No soy tan experto en bash así que pido ayuda para traducir ese pseudocódigo en algo funcional que me permita lograr lo que busco, ¿algún consejo y/o ayuda?
ACTUALIZAR
Quizás me equivoque, pero no sigo en absoluto lo que ustedes intentan hacer, así que aquí está el orden de los eventos:
# Run Replicator at mid day and mid night
# this two can run at same time since they are pushing data to different DBs
/var/sync/sync_bi/replicator/Replicator.php
/var/sync/sync_pfizer/replicator/Replicator.php
# Run sync at mid day and mid night
# this two should run one first and the second after 30 min since they are
# pushing data to the same DBs and second depends on first one
/var/sync/sync_bi/sync.php
/var/sync/sync_pfizer/sync.php
Teniendo esa información (y perdón por no aclararla primero), ¿cómo deberían funcionar sus soluciones? ¿Como los tienes ahora o necesitas cambiar algo para cubrir esto?
Respuesta1
Esto traduce tu pseudocódigo a bash:
#!/bin/bash
cd /var/sync
for dir in sync*/ ; do
( # Subshell to make "cd" not global.
cd "$dir"
[[ -f sync.php ]] && /usr/bin/php sync.php
if [[ -d replicator ]] ; then
cd replicator
[[ -f replicator.php ]] && /usr/bin/php replicator.php
fi
)
done
Sin embargo, ejecuta sync.php y replicator.php al mismo tiempo, pero originalmente querías llamarlos en momentos diferentes. Tal vez algo como
#!/bin/bash
run=$1
cd /var/sync
for dir in sync*/ ; do
( # Subshell to make "cd" not global.
cd "$dir"
[[ -f "$run" ]] && /usr/bin/php "${run##*/}"
)
done
llamado desde cron como
0 30 * * * /path/to/script.sh sync.php
0 0 * * * /path/to/script.sh replicator/Replicator.php
${run##*/}
elimina todo lo anterior al último /
, es decir, devuelve solo el nombre base.
Actualizar
Si desea ejecutar los scripts de la siguiente manera:
0:00 sync_a/sync.php
0:30 sync_a/replicator/Replicator.php
1:00 sync_b/sync.php
1:30 sync_b/replicator/Replicator.php
2:00 sync_c/sync.php
...
etc., puedes simplemente regenerar el crontab cada vez que cambies la estructura del directorio:
#!/bin/bash
add=$1
php=/usr/bin/php
hour=0
minute=0
update_time () {
(( minute += add ))
if (( minute >= 60 )) ; then
(( hour += minute / 60 ))
(( minute %= 60 ))
fi
if (( hour > 11 )) ; then
echo "Can't fit into a day!"
exit 1
fi
}
cd /var/sync
for dir in sync*/ ; do
if [[ -f "$dir"/sync.php ]] ; then
echo $minute $hour 0 0 0 "$php" /path/to/run.sh "$dir" sync.php
update_time
if [[ -f "$dir"/replicator/Replicator.php ]] ; then
echo $minute $hour 0 0 0 "$php" path/to/run.sh "$dir" Replicator.php
fi
update_time
fi
done
Llame como generate_crontab.sh 30
o similar. Tenga en cuenta que algunos de los directorios podrían omitirse después de un cambio, ya que los archivos están ordenados alfabéticamente.
run.sh
debería simplemente
dir=$1
script=$2
if [[ -f "$dir$script" ]] ; then
cd "$dir"
/usr/bin/php $script
fi
Respuesta2
El enfoque más simple sería dos find
comandos:
find /var/sync/ -name 'sync.php' -execdir php {} \;
find /var/sync/ -name 'Replicator.php' -execdir php {} \;
Eso buscará archivos llamados sync.php
o Replicator.php
, luego cd en su directorio principal y los ejecutará con php
. Podrías agregar los comandos directamente a tu crontab:
0 30 * * * find /var/sync/ -name 'sync.php' -execdir php {} \;
0 0 * * * find /var/sync/ -name 'Replicator.php' -execdir php {} \;
Si necesita que los Replicator
scripts se ejecuten con una pausa de 30 minutos entre ellos, puede hacer algo como:
find /var/sync/ -name 'Replicator.php' -execdir php {} \; -exec sh -c "sleep 30m" \;
Primero se ejecutará el script y luego se esperarán 30 minutos antes de pasar al siguiente.
Finalmente, si necesita asegurarse de que i) nunca tenga >1 sync.php
ejecutándose al mismo tiempo y ii) cada sync
secuencia de comandos se ejecute después de que finalice la secuencia de comandos correspondiente replicator
, puede usar esto como el comando que le da a cron:
find /var/sync/ -name 'Replicator.php' -execdir php {} \; ; find /var/sync/ -name 'sync.php' -execdir php {} \;
Lo anterior ejecutará cada uno de los Replicator.php
scripts primero (uno tras otro, no en paralelo) y, una vez que hayan terminado, ejecutará cada uno de los sync.php
scripts.
Respuesta3
{ cat > sync.sh
ln -s sync.sh replicator.sh
chmod +x sync.sh
} <<""
#!/bin/sh
cmd=${0%.*}
cmd=${cmd##*/}
find /var/sync -name "$cmd.php" -exec /usr/bin/php {} \;
Entonces, cuando llama a un script de shell, el shell establece el parámetro de shell especial con $0
el nombre del script que lee, como lo haría un programa en C. argv[0]
Ya está a mitad de camino porque está ejecutando dos scripts, cada uno con el nombre de su objetivo de ajuste. Pero no necesita dos scripts si tiene dos nombres, por lo que puede simplemente vincular simbólicamente el mismo script al segundo nombre y consultarlo $0
cuando lo llame.
Porque cron
solo necesitas
i=-30
for s in sync replicator
do echo "0 $((i+=30)) * * * /var/sync/$s.sh" | crontab
done