¿Cómo puedo decirle a cron que ejecute un comando cada dos días (impar / par)

45

Al configurar cron para ejecutar un comando cada dos días utilizando el campo "Día del mes", así:

1 22 */2 * * COMMAND

funciona cada vez que el día del mes es impar: 1,3,5,7,9 y así sucesivamente.

¿Cómo puedo configurar cron para que se ejecute en días del mes que son incluso como 2,6,8,10 y así sucesivamente (sin especificarlo literalmente, lo cual es problemático ya que cada mes tiene un número diferente de días en el mes)?

Freddie
fuente

Respuestas:

60

La sintaxis que probaste es realmente ambigua. Dependiendo de cuántos días haya en el mes, algunos meses se ejecutarán en días impares y otros en pares. Esto se debe a que la forma en que se calcula toma el número total de posibilidades y las divide. Puede anular este comportamiento extraño al especificar manualmente el rango de días y usar un número impar o par de días. Dado que los scripts de día par nunca se ejecutarían en el día 31 de meses más largos, no pierde nada usando 30 días como base para días pares, y al especificar específicamente dividirlo como si hubiera 31 días, puede forzar impar -Día de ejecución.

La sintaxis se vería así:

# Will only run on odd days:
0 0 1-31/2 * * command

# Will only run on even days:
0 0 2-30/2 * * command

Su preocupación acerca de que los meses no tengan la misma cantidad de días no es importante aquí porque no hay meses que tengan MÁS días que este y para el mal mes de febrero, el rango de fechas nunca coincidirá con el último día o dos, pero no hará daño tener En la lista.

El único 'problema' para este enfoque es que si está en un ciclo de días impares, después de los meses con 31 días, su comando también se ejecutará el primero del mes. Del mismo modo, si está forzando un ciclo uniforme, cada año bisiesto causará un ciclo de tres días y el final de febrero. Realmente no puede evitar el hecho de que cualquier patrón regular de "cada dos días" no siempre va a caer en días pares o impares en cada mes y de cualquier manera que lo fuerce, tendrá una carrera adicional o se perderá una carrera entre meses con recuentos de días coincidentes.

Caleb
fuente
1
Gracias, pero ¿qué pasará en meses como febrero donde solo tienes 28 días? La estrella realmente se encarga de eso, pero de hecho es ambigua.
freddie
3
@freddie: Vea mi respuesta editada ... pero no es un problema porque los valores fuera de rango simplemente serán ignorados, no pasará nada el 30 o 31 de febrero. Siempre. Puede especificar manualmente con una lista como, 0,2,4...,30,32,34y no importaría, los valores fuera de rango nunca coincidirían.
Caleb
1
¡Gracias! Entiendo, gracias por la respuesta informativa!
freddie
3
En el servidor Ubuntu 8.04, parece que la sintaxis que usa el día del mes cero no es válida (mal día del mes). Sin embargo, se acepta la siguiente sintaxis:0 0 2-30/2 * * command
1
A Fedora y RHEL 5,6,7 tampoco les gusta 0 como día del mes. Como señaló user31053: 0 0 2-30/2 * * commandfunciona como se esperaba.
NoelProf
2

Creo que una posibilidad es usar el día del año, como este:

# for odd days
test $(((`date +%j` % 2))) != 0 && command

# for even days
test $(((`date +%j` % 2))) == 0 && command

Está probado para sistemas Unix y Linux.

Jordi
fuente
Prefiero esta respuesta porque omite el problema con la cantidad impar de días con algunos meses. De todos modos sugiero la notación en dólares en lugar de retroceder:test $(($(date +%j) % 2)) == 0 && command
caligari
He revisado que% j no es la fecha juliana, por lo que el mejor código que evita la transición de Año Nuevo debe calcularse con segundos:test $(($(date +%s) / 86400 % 2)) == 0 && command
caligari
! Gracias por los comentarios! Nuestra propuesta funcionó para nosotros como un encanto. Utilizamos ese esquema para ejecutar todos los días crons en diferentes nodos del servidor, todos ellos comparten el mismo crontab, pero el script solo permite ejecutar el script en uno de ellos. Sin embargo, si necesitamos hacerlo más específico, consideraremos su propuesta. ¡¡¡Gracias!!!
Jordi
1

Verifiquemos todos los días si es un "otro" :-) ( bcprograma requerido)

0 0 * * * test $(echo `date +%s` / 86400 % 2 == 0 |bc) -eq 0 && command

(No estoy seguro de que el código aparezca correctamente. La date +%sparte está entre los apóstrofos posteriores).

usuario37264
fuente
¡Esto se ejecutará cada dos días, pero no responde la pregunta! Todavía se ejecutará a veces en días impares, a veces en días pares, dependiendo del mes. Esto tiene el mismo resultado que el código en la pregunta, solo está llegando al resultado haciendo sus propias matemáticas en los segundos desde la época. Esto se ejecuta en días pares desde la época pero no en días pares de nuestro calendario.
Caleb
1

En general, lo ejecutaría todos los días y que el script use la lógica para determinar si debería ejecutarse hoy.

Hacer un archivo de estado simple que indique cuándo fue la última ejecución y luego comparar funcionaría muy fácilmente.

Si necesita ejecutarse a través de diferentes fuentes, haga que dependa de argumentos.

Klas Mattsson
fuente