← Automatización con Scripts en Bash Bloque 8 · Tareas programadas · Tema 33 de 42

Tema 33

Programar tareas con cron

Un script de respaldo que hay que acordarse de ejecutar no es del todo automático. El último paso de la automatización es quitar a la persona del medio: que el respaldo corra cada noche a las 2:30, que los registros viejos se compriman cada domingo y que el informe llegue los lunes a las 8, sin que nadie haga nada. En Linux, la herramienta clásica para eso es cron. Este tema explica cómo se le indica qué ejecutar y cuándo, cómo se leen sus expresiones de tiempo, y las particularidades de su línea de comando que sorprenden a todos la primera vez.

1. Qué es cron

cron es un servicio del sistema, un demonio, que se despierta una vez por minuto, revisa unas tablas de tareas llamadas crontabs y ejecuta las que corresponden a ese minuto. Cada línea de un crontab dice cuándo y qué: una expresión de tiempo y un comando.

El nombre viene del griego chronos, tiempo. Existe desde los primeros Unix, y casi todas las distribuciones de Linux lo traen instalado y en marcha: Debian y Ubuntu usan una versión derivada del cron de Paul Vixie, y Fedora, RHEL y sus derivadas usan cronie, otra variante del mismo. Para lo que se ve en este tema se comportan igual; donde difieren, lo indicaremos.

terminal · comprobar que cron está en marcha
$ systemctl is-active cron          # en Fedora y RHEL, el servicio se llama crond
active

Cron es la forma más difundida, pero no la única, de programar tareas. Los temporizadores de systemd y el comando at, que veremos en el tema 35, cubren casos que cron resuelve mal. Pero las expresiones de cron aparecen en todas partes, desde los servidores de integración continua hasta los servicios en la nube, y vale la pena dominarlas.

2. El crontab de cada usuario

Cada usuario tiene su propio crontab, y sus tareas se ejecutan con sus permisos. No se edita directamente el archivo donde se guarda, sino con el comando crontab:

ComandoEfecto
crontab -eAbre el crontab en un editor; al guardar, lo valida e instala.
crontab -lMuestra el crontab actual. Si no hay ninguno, falla con no crontab for usuario.
crontab archivoReemplaza el crontab por el contenido de un archivo.
crontab -Lo reemplaza por lo que llega a la entrada estándar.
crontab -rBorra el crontab entero, sin preguntar.
sudo crontab -u ana -lOpera sobre el crontab de otro usuario; requiere ser root.
terminal
$ crontab -l
no crontab for ana
$ crontab -e
no crontab for ana - using an empty one
crontab: installing new crontab
$ crontab -l
# m h  dom mon dow   command
30 2 * * * /home/ana/bin/respaldar -q

crontab -e usa el editor que indican las variables VISUAL o EDITOR; en Debian y Ubuntu, la primera vez pregunta cuál usar. Al guardar, comprueba la sintaxis de cada línea, y si encuentra un error no instala nada y ofrece volver a editar. Esa validación es la razón para no tocar a mano los archivos de /var/spool/cron/, donde cron guarda los crontabs: un error escrito allí pasa sin aviso, y la tarea simplemente no corre.

Las líneas que empiezan con # son comentarios, y las vacías se ignoran. Conviene usar los comentarios: dentro de un año, 15 4 * * 0 /usr/local/bin/x2 no le dirá nada a nadie.

La r está al lado de la e

En el teclado, -r queda junto a -e, y crontab -r borra todas las tareas sin confirmación ni posibilidad de deshacer. Antes de cualquier cambio importante, guarda una copia con crontab -l > ~/crontab.respaldo, que se restaura con crontab ~/crontab.respaldo. cronie acepta además crontab -ri, que pide confirmación antes de borrar.

3. Los cinco campos de tiempo

Una línea de crontab tiene cinco campos de tiempo, separados por espacios o tabulaciones, y después el comando, que ocupa el resto de la línea:

La línea 30 2 * * 1-5 seguida de un comando. El primer campo, 30, es el minuto, de 0 a 59; el segundo, 2, es la hora, de 0 a 23; el tercero, un asterisco, es el día del mes, de 1 a 31; el cuarto, otro asterisco, es el mes, de 1 a 12; el quinto, 1-5, es el día de la semana, de 0 a 7, donde 0 y 7 son el domingo. Después va el comando que se ejecuta 30 2 * * 1-5 comando minuto hora día del mes mes día de la semana el resto de la línea 0–59 0–23 1–31 1–12 0–7 0 y 7: domingo o jan … dec
Cada campo con sus valores posibles. Esta línea ejecuta el comando a las 2:30, de lunes a viernes.

Cron compara cada minuto la hora actual con los cinco campos, y ejecuta el comando si coinciden todos. El asterisco significa «cualquier valor», de modo que 30 2 * * 1-5 se lee: «en el minuto 30, de la hora 2, cualquier día del mes, cualquier mes, si el día de la semana está entre el lunes y el viernes». Las horas van de 0 a 23, sin a.m. ni p.m.: las 2 de la tarde son 14.

Una ayuda para no confundir el orden es leerlo de lo más pequeño a lo más grande: minuto, hora, día, mes. El día de la semana, que no encaja en esa escala, va al final. Mucha gente copia sobre sus tareas un comentario como el que pone Debian en un crontab nuevo, # m h dom mon dow command, que recuerda el orden.

4. Valores, listas, rangos y pasos

Cada campo puede contener algo más que un número o un asterisco:

FormaEjemploSignifica
Asterisco*Cualquier valor del campo.
Valor5Solo ese valor.
Lista1,15Cualquiera de los valores, separados por comas y sin espacios.
Rango9-17Todos los valores entre los dos, incluidos.
Paso sobre todo el campo*/15Uno de cada 15 desde el primer valor: en minutos, 0, 15, 30 y 45.
Paso sobre un rango0-30/100, 10, 20 y 30.
Combinaciones0-10,30,50-59Una lista de valores y rangos.
Nombresmon, janLas tres primeras letras, en inglés, para el mes y el día de la semana.

Los pasos cuentan desde el comienzo del rango, no desde el momento en que se instaló la tarea, y empiezan de nuevo en cada hora, día o mes. Por eso */15 en minutos es regular, porque 15 divide a 60, pero */7 no lo es: produce 0, 7, 14… 49, 56, y luego vuelve a 0, solo cuatro minutos después. Del mismo modo, */2 en el día del mes da los días impares, y el 31 y el 1 quedan seguidos. Para un intervalo exacto que no divide la unidad, cron no es la herramienta adecuada.

Los nombres, como mon-fri, se leen mejor que los números, pero el cron de Debian no los admite en rangos ni en listas. Si el crontab debe funcionar en cualquier sistema, conviene usar números en esos casos.

5. Expresiones habituales

Con esas piezas se arman casi todos los horarios que se necesitan en la práctica:

ExpresiónSe ejecuta
* * * * *Cada minuto.
*/5 * * * *Cada 5 minutos.
0 * * * *Al comienzo de cada hora.
15 */2 * * *A las 0:15, 2:15, 4:15… cada dos horas.
30 2 * * *Todos los días a las 2:30.
0 8,20 * * *Todos los días a las 8:00 y a las 20:00.
0 9-18 * * 1-5Cada hora en punto de 9 a 18, de lunes a viernes.
0 12 * * 1,3,5Lunes, miércoles y viernes al mediodía.
0 0 * * 0Los domingos a medianoche.
0 8 1 * *El día 1 de cada mes a las 8:00.
0 3 1 */3 *El 1 de enero, abril, julio y octubre a las 3:00.
0 0 1 1 *Una vez al año, el 1 de enero a medianoche.

Un detalle que conviene fijar: para que algo ocurra a una hora determinada, el minuto debe tener un valor. * 2 * * * no significa «a las 2», sino «cada minuto entre las 2:00 y las 2:59»: sesenta ejecuciones.

Y otro, si muchas máquinas o muchas tareas usan la misma expresión: todos eligen 0 * * * * o 0 0 * * *, y a esa hora coinciden todas a la vez. Para tareas que pueden correr a cualquier minuto, elegir uno poco redondo, como 17 o 43, reparte la carga. Es lo que hace el propio /etc/crontab de Debian, que ejecuta las tareas horarias en el minuto 17.

6. Día del mes o día de la semana

Hay una regla de cron que contradice la lectura natural de una expresión. Los campos se combinan con «y», salvo un caso: si el día del mes y el día de la semana tienen los dos un valor distinto de *, la tarea se ejecuta cuando coincide cualquiera de los dos.

la regla del «o»
0 9 13 * 5    # NO es «los viernes 13»: es todos los días 13, y además todos los viernes
0 9 1 * 1     # el día 1 de cada mes, y además todos los lunes

Para expresar «el primer lunes del mes» o «los viernes 13», se programa la tarea con uno de los dos campos y se agrega una condición en el comando. El primer lunes de un mes es el único lunes entre los días 1 y 7; el último día del mes es aquel cuyo día siguiente es 1:

condiciones en el comando
# El primer lunes de cada mes: días 1 a 7, y hoy es lunes (date +%u da 1)
0 9 1-7 * *   [ "$(date +\%u)" = 1 ] && /home/ana/bin/reunion-mensual

# Los viernes 13
0 9 13 * *    [ "$(date +\%u)" = 5 ] && /home/ana/bin/supersticion

# El último día de cada mes: mañana es día 1
0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /home/ana/bin/cierre-mensual

Las barras antes de cada % no son un descuido: en un crontab, el % tiene un significado especial, como se explica en la sección 8. La condición también puede ir dentro del script, como su primera comprobación, y es lo más claro cuando la regla es complicada.

7. Las cadenas especiales

En lugar de los cinco campos, una línea puede empezar con una cadena que abrevia los horarios más comunes:

CadenaEquivale aSe ejecuta
@reboot—Una vez, cuando arranca cron, es decir, al iniciar el sistema.
@hourly0 * * * *Al comienzo de cada hora.
@daily, @midnight0 0 * * *Todos los días a medianoche.
@weekly0 0 * * 0Los domingos a medianoche.
@monthly0 0 1 * *El día 1 de cada mes a medianoche.
@yearly, @annually0 0 1 1 *El 1 de enero a medianoche.
cadenas especiales en un crontab
@reboot  /home/ana/bin/montar-red
@weekly  /home/ana/bin/limpiar-descargas

@reboot es la única que agrega algo que los cinco campos no pueden expresar. Hay que tener en cuenta que corre muy temprano en el arranque, cuando quizás la red o algún disco todavía no están disponibles: un script lanzado así debe estar preparado para esperar o reintentar. Para servicios que deben quedar en marcha, un servicio de systemd es más adecuado.

8. La línea del comando

El comando de una línea de crontab no se ejecuta en una terminal como la tuya. Cron se lo pasa a /bin/sh, desde el directorio personal del usuario y con un entorno mínimo. De eso se derivan varias reglas, algunas sorprendentes:

Un crontab puede definir variables, en líneas con la forma NOMBRE=valor, que valen para las tareas que siguen. Las más útiles son SHELL, PATH y MAILTO, la dirección a la que va la salida; con MAILTO="", no se envía nada:

variables al comienzo del crontab
SHELL=/bin/bash
PATH=/home/ana/bin:/usr/local/bin:/usr/bin:/bin
MAILTO=ana@example.com

30 2 * * *   respaldar -q
*/10 * * * * flock -n /tmp/sincronizar.lock sincronizar

Ojo con un detalle: estas asignaciones no son de shell, y cron no expande nada en ellas. PATH=$HOME/bin:$PATH deja en PATH exactamente ese texto, con el signo de pesos, y no se encuentra ningún programa. El valor se escribe completo.

La última línea del ejemplo muestra otro cuidado: si una tarea que corre cada 10 minutos alguna vez tarda más de 10, cron lanza la siguiente igual, y quedan dos ejecuciones simultáneas. Envolverla con flock -n, como vimos en el tema 27, hace que la segunda termine enseguida si la primera sigue en marcha. El entorno de cron, la forma de capturar la salida en un archivo de registro y cómo averiguar por qué una tarea no hizo lo que debía son el tema del tema 34.

El horario de verano

Cron usa la hora local del sistema. En las zonas con horario de verano, el día del cambio una hora no existe o se repite, y una tarea programada a las 2:30 puede no ejecutarse o adelantarse, según la implementación. Para tareas importantes, conviene evitar el intervalo entre la 1 y las 3 de la madrugada, o usar en el servidor la hora UTC, que no tiene cambios. cronie permite además fijar la zona de cada crontab con la variable CRON_TZ.

9. Los crontabs del sistema

Además de los crontabs de usuario, hay crontabs del sistema, que administra root y que tienen un campo más: después de los cinco de tiempo, el usuario con el que se ejecuta el comando.

UbicaciónContenido
/etc/crontabEl crontab principal del sistema, con el campo de usuario.
/etc/cron.d/Archivos con el mismo formato, uno por aplicación o tarea. Es donde los paquetes y las herramientas de configuración instalan las suyas.
/etc/cron.hourly/, daily/, weekly/, monthly/Scripts ejecutables, sin expresión de tiempo: se ejecutan todos, uno tras otro, con la frecuencia que indica el directorio.
/etc/cron.d/respaldo
# Respaldo nocturno de la base de datos
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=admin@example.com

30 2 * * *   backup   /usr/local/bin/respaldar -q

Para una tarea del sistema, un archivo propio en /etc/cron.d/ tiene ventajas sobre el crontab de root: se instala y se borra como cualquier archivo, se puede guardar en un repositorio y, como indica el usuario, permite ejecutar la tarea con una cuenta sin privilegios, como backup en el ejemplo. Pero tiene reglas estrictas, y si no se cumplen, cron ignora el archivo en silencio: debe pertenecer a root, no debe poder modificarlo nadie más, y la última línea debe terminar con un salto de línea. En Debian y Ubuntu, además, el nombre solo puede tener letras, dígitos, guiones y guiones bajos: respaldo.cron se ignora.

Los directorios cron.daily y sus hermanos los recorre el comando run-parts, con las mismas reglas de nombres, de modo que un script llamado limpiar.sh tampoco se ejecuta en Debian. En muchos sistemas los lanza anacron, una herramienta pensada para equipos que no están siempre encendidos: si a la hora prevista la máquina estaba apagada, anacron ejecuta la tarea diaria cuando vuelve a arrancar. Cron, en cambio, nunca recupera las ejecuciones perdidas: si el portátil estaba cerrado a las 2:30, el respaldo de esa noche no se hizo.

Por último, el administrador puede decidir quién usa cron, con los archivos /etc/cron.allow y /etc/cron.deny: si existe el primero, solo los usuarios que figuran en él pueden tener un crontab; si no, pueden todos salvo los del segundo.

10. Probar una tarea nueva

Una tarea de cron que falla no avisa a nadie: simplemente no ocurre, y uno se entera semanas después, cuando necesita el respaldo. Antes de dar una tarea por terminada:

  1. Ejecutar el comando a mano, exactamente como figura en el crontab, con rutas absolutas y los % sin la barra. Si falla aquí, fallará en cron. a mano
  2. Programarla para dentro de dos minutos, con un horario de prueba como 47 10 * * *, o cada minuto, y comprobar que hizo lo que debía: que el archivo apareció, que el registro tiene la línea nueva. en cron
  3. Mirar el registro de cron, que anota cada ejecución con el usuario y el comando. sistema
  4. Poner el horario definitivo, y revisar el resultado tras la primera ejecución real. definitivo
terminal · una prueba de un minuto
$ (crontab -l; echo '* * * * * date >> /tmp/prueba-cron.txt') | crontab -
# … un par de minutos después:
$ cat /tmp/prueba-cron.txt
jue 15 oct 2026 10:47:01 -03
jue 15 oct 2026 10:48:01 -03
$ journalctl -u cron --since '10 min ago'         # crond en Fedora y RHEL
oct 15 10:47:01 servidor CRON[48211]: (ana) CMD (date >> /tmp/prueba-cron.txt)
oct 15 10:48:01 servidor CRON[48263]: (ana) CMD (date >> /tmp/prueba-cron.txt)
$ crontab -l | grep -v prueba-cron | crontab -   # quitar la línea de prueba

Las ejecuciones empiezan en el segundo 1 de cada minuto, y el registro confirma que cron lanzó el comando. Lo que no confirma es que el comando haya funcionado: para eso hay que mirar su resultado o su propio registro. En los sistemas sin journald, las mismas líneas están en /var/log/syslog o en /var/log/cron. La primera línea del ejemplo es, además, la forma de agregar una tarea sin abrir un editor, la que usa el script de la sección siguiente.

11. Un script completo

Editar el crontab a mano está bien para una tarea; pero cuando un script de instalación tiene que dejar programadas las suyas, abrir un editor no es una opción, y agregar la línea con echo la duplica en cada ejecución, el problema que vimos en el tema 27. Este script agrega, cambia y quita tareas del crontab del usuario, identificándolas por un nombre. Es idempotente: ejecutarlo dos veces con los mismos datos no cambia nada.

cron-tarea
#!/usr/bin/env bash
#
# cron-tarea — agrega, cambia y quita tareas del crontab del usuario,
# identificadas por un nombre, sin duplicarlas.
#
# Uso: cron-tarea poner NOMBRE 'MIN HORA DÍA MES DÍASEM' COMANDO
#      cron-tarea quitar NOMBRE
#      cron-tarea listar
#
set -euo pipefail

prog="${0##*/}"
marca='# cron-tarea:'
respaldo="$HOME/.crontab.anterior"

uso() {
  echo "Uso: $prog poner NOMBRE 'MIN HORA DÍA MES DÍASEM' COMANDO" >&2
  echo "     $prog quitar NOMBRE" >&2
  echo "     $prog listar" >&2
  exit 2
}
error() { echo "$prog: $*" >&2; exit 1; }

horario_valido() {
  local campo='[0-9A-Za-z*,/-]+'
  local cinco="^$campo +$campo +$campo +$campo +$campo\$"
  local especial='^@(reboot|hourly|daily|midnight|weekly|monthly|yearly|annually)$'
  [[ "$1" =~ $cinco || "$1" =~ $especial ]]
}

leer_crontab() {                      # el crontab actual; si no hay, nada
  crontab -l 2>/dev/null || true
}

editar() {                            # aplica el programa awk $1 al crontab actual
  printf '%s' "${actual:+$actual$'\n'}" | MARCA="$marca$nombre" LINEA="${linea:-}" awk "$1"
}

instalar() {                          # reemplaza el crontab por $1, guardando el anterior
  printf '%s\n' "$actual" > "$respaldo"
  printf '%s\n' "$1" | crontab -
}

(( $# >= 1 )) || uso
accion="$1"; shift

case "$accion" in
  poner)
    (( $# == 3 )) || uso
    nombre="$1" horario="$2" comando="$3"
    [[ "$nombre" =~ ^[a-z0-9][a-z0-9-]*$ ]] || error "nombre no válido: $nombre"
    horario_valido "$horario" || error "horario no válido: $horario"
    [[ "$comando" != *$'\n'* ]] || error "el comando debe ocupar una sola línea"
    comando="${comando//%/'\%'}"      # en un crontab, % significa salto de línea
    linea="$horario $comando"

    actual=$(leer_crontab)
    nuevo=$(editar '
      cambiar                { cambiar = 0; print ENVIRON["LINEA"]; next }
      $0 == ENVIRON["MARCA"] { cambiar = 1; hay = 1 }
                             { print }
      END { if (!hay) { print ENVIRON["MARCA"]; print ENVIRON["LINEA"] } }')

    if [[ "$nuevo" == "$actual" ]]; then
      echo "La tarea $nombre ya estaba programada así; no hay cambios." >&2
    else
      instalar "$nuevo"
      echo "Tarea $nombre programada: $linea" >&2
    fi
    ;;
  quitar)
    (( $# == 1 )) || uso
    nombre="$1"
    actual=$(leer_crontab)
    nuevo=$(editar '
      saltar                 { saltar = 0; next }
      $0 == ENVIRON["MARCA"] { saltar = 1; next }
                             { print }')

    if [[ "$nuevo" == "$actual" ]]; then
      echo "No había ninguna tarea llamada $nombre; no hay cambios." >&2
    else
      instalar "$nuevo"
      echo "Tarea $nombre quitada." >&2
    fi
    ;;
  listar)
    leer_crontab | awk -v m="$marca" '
      index($0, m) == 1 { nombre = substr($0, length(m) + 1); getline; printf "%-10s %s\n", nombre, $0 }'
    ;;
  *) uso ;;
esac
terminal
$ crontab -l
# Mis tareas
MAILTO=ana@example.com
$ cron-tarea poner respaldo '30 2 * * *' '/home/ana/bin/respaldar -q'
Tarea respaldo programada: 30 2 * * * /home/ana/bin/respaldar -q
$ cron-tarea poner informe '0 8 * * 1-5' '/home/ana/bin/informe > /tmp/informe-$(date +%F).txt'
Tarea informe programada: 0 8 * * 1-5 /home/ana/bin/informe > /tmp/informe-$(date +\%F).txt
$ cron-tarea poner respaldo '30 2 * * *' '/home/ana/bin/respaldar -q'
La tarea respaldo ya estaba programada así; no hay cambios.
$ cron-tarea poner respaldo '0 3 * * *' '/home/ana/bin/respaldar -q'
Tarea respaldo programada: 0 3 * * * /home/ana/bin/respaldar -q
$ crontab -l
# Mis tareas
MAILTO=ana@example.com
# cron-tarea:respaldo
0 3 * * * /home/ana/bin/respaldar -q
# cron-tarea:informe
0 8 * * 1-5 /home/ana/bin/informe > /tmp/informe-$(date +\%F).txt
$ cron-tarea listar
respaldo   0 3 * * * /home/ana/bin/respaldar -q
informe    0 8 * * 1-5 /home/ana/bin/informe > /tmp/informe-$(date +\%F).txt
$ cron-tarea poner limpiar '5 * * *' 'rm -f /tmp/app/*'
cron-tarea: horario no válido: 5 * * *
$ cron-tarea quitar informe
Tarea informe quitada.
$ cron-tarea quitar informe
No había ninguna tarea llamada informe; no hay cambios.

El script pierde un único caso: si el usuario borra a mano la marca y deja la línea, el script ya no la reconoce, y un nuevo poner la duplicaría. Es el compromiso de cualquier herramienta que comparte un archivo con las personas.

12. Errores frecuentes

EscribisteQué pasaCorrección
crontab -r en lugar de -eSe borran todas las tareas, sin preguntar.Guardar antes una copia con crontab -l > archivo.
* 2 * * * para «a las 2»Se ejecuta cada minuto entre las 2:00 y las 2:59.0 2 * * *
date +%F en la líneaEl comando se corta en el %; el resto va a su entrada.date +\%F, o llevarlo a un script.
0 9 13 * 5 para «los viernes 13»Se ejecuta todos los días 13 y todos los viernes.0 9 13 * * y comprobar el día en el comando.
[[ ]], arrays o {1..5} en la línea/bin/sh puede ser dash, que no los entiende.Un script con #!/usr/bin/env bash, o SHELL=/bin/bash.
Un programa sin ruta, como respaldarCon el PATH de cron no se encuentra.Ruta absoluta, o un PATH completo al comienzo del crontab.
PATH=$HOME/bin:$PATH en el crontabCron no expande variables: queda el texto literal.Escribir el valor completo.
Una tarea */5 que a veces tarda más de 5 minutosSe acumulan ejecuciones simultáneas.flock -n archivo comando
/etc/cron.d/respaldo.cronEn Debian y Ubuntu, un nombre con punto se ignora./etc/cron.d/respaldo
La última línea sin salto de línea finalEsa línea se ignora.Terminar el archivo con un salto de línea.
Editar los archivos de /var/spool/cron/Sin validación: un error pasa en silencio.crontab -e o crontab archivo.

13. Resumen

14. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Por qué no conviene editar a mano los archivos de /var/spool/cron/?

Porque así se saltea la validación que hace el comando crontab: una línea mal escrita no se detecta, y la tarea simplemente no se ejecuta. Además, cron podría no advertir el cambio.

2. ¿Cuándo se ejecuta 45 23 * * 5?

Todos los viernes a las 23:45.

3. ¿Cómo se escribe «cada 15 minutos, de 8 a 18, de lunes a viernes»?

*/15 8-18 * * 1-5. Se ejecuta en los minutos 0, 15, 30 y 45 de cada hora entre las 8 y las 18, incluida la hora 18, de modo que la última ejecución del día es a las 18:45.

4. ¿Qué tiene de raro */7 en el campo de los minutos?

Que el paso empieza de nuevo cada hora: produce 0, 7, 14… 56, y luego 0 otra vez, solo cuatro minutos después. Como 7 no divide a 60, los intervalos no son todos iguales.

5. ¿Cuándo se ejecuta 0 9 1 * 1?

A las 9 del día 1 de cada mes, y además a las 9 de todos los lunes: como el día del mes y el día de la semana tienen valores, basta con que coincida uno de los dos.

6. ¿Qué diferencia hay entre * 3 * * * y 0 3 * * *?

La primera se ejecuta cada minuto entre las 3:00 y las 3:59, sesenta veces; la segunda, una sola vez, a las 3:00.

7. ¿Por qué falla 0 0 * * * tar czf /respaldos/casa-$(date +%F).tgz /home/ana en un crontab?

Porque cron convierte el % en un salto de línea, y el comando se corta en date +. Hay que escribir \%F. Además, convendría usar la ruta absoluta de tar o llevar el comando a un script.

8. ¿Qué ocurre con una línea PATH=$HOME/bin:/usr/bin:/bin en un crontab?

Cron no expande variables en sus asignaciones, de modo que el PATH contiene literalmente $HOME/bin, que no es ningún directorio. Hay que escribir la ruta completa, como /home/ana/bin.

9. ¿Qué diferencia hay entre una línea de /etc/cron.d/ y una del crontab de un usuario?

La de /etc/cron.d/ tiene un campo más, después de los cinco de tiempo: el usuario con el que se ejecuta el comando. En el crontab de un usuario, las tareas se ejecutan siempre como ese usuario.

10. ¿Por qué cron-tarea pasa el comando a awk con ENVIRON y no con -v?

Porque -v interpreta las secuencias con barra invertida, y el \% que el script acaba de escribir se convertiría otra vez en %. ENVIRON entrega el valor exacto.

Para practicar

  1. Escribe la expresión de cron para cada caso: todos los días a las 6:45; cada 10 minutos; los sábados y domingos a las 10; el día 15 de cada mes a mediodía; cada dos horas, a la hora en punto, solo los días hábiles.
  2. Guarda una copia de tu crontab, agrega con crontab -e una tarea que escriba la fecha en un archivo cada minuto, comprueba el archivo y el registro de cron, y luego restaura la copia.
  3. Agrega una tarea que use date +%H:%M sin escapar los %, y observa qué queda en el archivo de salida. Luego corrígela.
  4. Programa una tarea para «el último viernes de cada mes» usando una condición en el comando.
  5. Usa cron-tarea para programar, cambiar y quitar una tarea, y comprueba con crontab -l que las líneas que escribiste a mano no se modificaron.
  6. Agrega a cron-tarea una acción pausar NOMBRE, que comente la línea de la tarea sin quitarla, y otra reanudar NOMBRE, que la vuelva a activar.