En GNU/Linux, el programa cron es un demonio (o "servicio", como prefieran llamarle) para ejecutar tareas programadas, llamadas "cronjobs". En ocasiones puede sucedernos que creamos un cronjob, esperamos su ejecución programada, y en el log (/var/log/cron si se trata de Red Hat/Fedora/CentOS o /var/log/syslog si se trata de una distribución basada en Debian) nos encontramos con diferente tipo de errores, por ejemplo: "(CRON) bad command", "/bin/sh: Desktop: command not found", "Error: bad username; while reading" o "/bin/sh: root: command not found".

Todos estos mensajes de error se producen cuando tenemos un problema en la sintaxis del cronjob.



El demonio cron levanta tareas programadas de diferentes fuentes. Primero busca los crontabs de los usuarios, los cuales se almacenan en el directorio /var/spool/cron en Red Hat/Fedora/CentOS, o el en directorio /var/spool/cron/crontabs en Debian y derivados, cuyos nombres de archivo coinciden con los nombres de login de los usuarios. Luego levanta los crontabs de sistema, los cuales se almacenan en el archivo /etc/crontab y en el directorio /etc/cron.d.

La definición de tareas programadas es propensa a errores ya que los crontabs de usuario tienen una sintaxis diferente a la de los crontabs de sistema. Los crontabs de sistema incluyen el campo "usuario" para determinar el UID (user ID del proceso) con el que se ejecuta cada tarea. En los crontabs de usuario este campo no es necesario, porque cada usuario sólo puede ejecutar tareas programadas a su nombre.

Por otro lado, desde el crontab de sistema se desprenden los directorios /etc/cron.hourly/, /etc/cron.daily/, /etc/cron.weekly/ y /etc/cron.monthly/. Los cuales se utilizan para ejecutar tareas automáticamente cada hora, día, semana o mes. Pero estos directorios no guardan cronjobs, sino directamente los scripts o binarios que se desean ejecutar.

Dentro del crontab de sistema se encuentran diferentes tareas programadas donde se definen el minuto en que se ejecutan los scrips cada hora (cron.hourly); la hora y minuto en que se ejecutan los script diarios (cron.daily); y la hora, minuto y día en que se ejecutan los scripts semanales y mensuales (cron.weekly y crond.monthly). Por ejemplo, si queremos ejecutar un script diariamente sólo es necesario almacenarlo (o poner un link simbólico) dentro del directorio /etc/cron.daily. Pero si deseamos ejecutarlo una vez al día, y a una hora en particular, necesitaremos definir nuestra propia tarea programa en nuestro crontab, o definir un cronjob de sistema en el archivo /etc/crontab o dentro del directorio /etc/cron.d/.

Esta cantidad de archivos y directorios pueden provocar confusión y llevarnos a cometer un error, como por ejemplo definir una tarea de sistema sin campo usuario, o una tarea de usuario con campo usuario (ambas incorrectas).

Crontab del usuario

Las tareas programadas de los usuarios se definen en el área spool, tal como se ha mencionado anteriormente. La forma más sencilla de editar el crontab del usuario es utilizando la herramienta crontab, la cual se utiliza para tal propósito.

Podemos listar las tareas programadas mediante crontab -l;

pepe@servidor07:~$ crontab -l
no crontab for pepe

Para editar el crontab debemos utilizar crontab -e, por ejemplo en Ubuntu Server:

pepe@servidor07:~$ crontab -e
no crontab for pepe - using an empty one

Select an editor.  To change later, run 'select-editor'.
  1. /bin/ed
  2. /bin/nano        <---- easiest
  3. /usr/bin/vim.basic
  4. /usr/bin/vim.tiny

Choose 1-4 [2]:

La primera vez que se edita pregunta qué editor deseamos utilizar (nano por defecto) y guarda nuestra preferencia en el archivo ~/.selected_editor.

Por ejemplo, selecciono 4 para utilizar el editor vi:

# m h  dom mon dow   command
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
-- INSERT --

Suponiendo que el usuario "pepe" desea ejecutar cada 5 minutos el script "tarea01.sh", el cual se encuentra en el directorio /home/pepe/tareas_programadas/ y desea enviar tanto la salida estándar como la standard error al archivo de log /var/log/tarea01.log, la sintaxis es la siguiente:

# m h  dom mon dow   command
*/5 * * * * ~/tareas_programadas/tarea01.sh 2>&1 >> /var/log/tarea01.log
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
-- INSERT --

Las líneas que comienzan con el caracter '#' son comentarios. La primera columna indica el minuto (de 0 a 59, "*/5" para que se ejecute cada 5 minutos), el resto de las columnas indican la hora (de 0 a 23), el día del mes (de 1 a 28 ó 31 dependiendo del mes), el mes (de 1 a 12) y el día de la semana (de 0 a 7, donde 0 y 7 equivalen a domingo).

En los campos día de la semana y mes es posible utilizar los tres primeros caracteres del nombre (sin importar el case). Por ejemplo "mon" o "Mon" para el lunes, "jun" para junio, etc.

También se permite utilizar listas y rangos, por ejemplo en el campo "día del mes" se podría especificar "2-4,8-10" para que la tarea se ejecute los días 2, 3, 4, 8, 9 y 10.

Para mayor información referirse al manual de crontab:

man 5 crontab

Luego de guardar y cerrar el editor podemos ver el listado de tareas nuevamente, para verificar que se haya actualizado correctamente:

pepe@servidor07:~$ crontab -l
# m h  dom mon dow   command
*/5 * * * * /home/pepe/tareas_programadas/tarea01.sh 2>&1 >> /var/log/tarea01.log

Crontab de sistema

El crontab de sistema se utiliza generalmente cuando se requiere que una tarea se ejecute con privilegios de administrador (root). La definición es igual a la de un crontab de usuario con la excepción de que se debe incluir el campo "usuario". Es posible editar directamente el archivo /etc/crontab aunque es más prolijo agregar una definición dentro del directorio /etc/cron.d/, el cual sirve para almacenar cronjobs de sistema para diferentes usuarios. Por ejemplo podemos crear el archivo /etc/cron.d/tarea01 con el siguiente contenido:

*/5 * * * * root /home/pepe/tareas_programadas/tarea01.sh 2>&1 >> /var/log/tarea01.log

Notar que luego de la definición del día de la semana se ha agregado el nombre de usuario con el que se ejecutará el proceso que lleve a cabo al tarea programada, en este caso "root".

Cada vez que definimos una nueva tarea programada dentro del directorio /etc/cron.d/ el servicio cron la levanta automáticamente. No es necesario reiniciar el servicio.

Directorios cron.hourly, cron.daily, cron.weekly y cron.monthly

Cada versión de toda distribución GNU/Linux utiliza diferentes horarios para ejecutar los scripts dentro de los directorios /etc/cron.hourly/, /etc/cron.daily/, /etc/cron.weekly/ y /etc/cron.monthly/. Por ejemplo el sistema que utilizo como ejemplo (Ubuntu Server) ejecuta cron.hourly en el minuto 17, cron.daily a las 6:25, cron.weekly los domingos a las 6:47 y cron.monthly el primer día del mes a las 6:52:

root@servidor07:/home/pepe# cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user  command
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#


Recuerden que estos directorios no poseen definiciones de tareas programadas (cronjobs) sino que directamente contienen los scripts o binarios a ejecutar.

Errores comunes

El demonio cron lleva un log de actividad en el archio /var/log/cron o directamente en el syslog. Algunos errores comunes al definir cronjobs son los siguientes.

Asterioscos de más
*/5 * * * * * root /root/tareas_programadas/tarea01.sh 2>&1 >> /var/log/tarea01.sh

Un error común es agregar un asterisco de más en la definición de timing de la tarea programada. En algunas distribuciones, por ejemplo en Slackware 13.1 veremos el error:

/bin/sh: Desktop: command not found

En otras versiones de cron veremos el error:

(CRON) bad command
Campo usuario de más
*/5 * * * * root /root/tareas_programadas/tarea01.sh 2>&1 >> /var/log/tarea01.sh

Este es el caso en el que agregamos el campo usuario en el crontab del usuario. Por supuesto, si se tratase de un cronjob de sistema la sintaxis sería la correcta. El error en el log suele ser:

/bin/sh: root: command not found

En algunas versiones de cron, por ejemplo la que se incluye en Slackware 13.1, no utiliza el campo "usuario" en los cronjobs de sistema, ya que por defecto se ejecutan como root. Por lo tanto veremos el mismo error y debemos remover el campo usuario para solucionar el problema.

Campo usuario faltante

El caso inverso al anterior en el que nos falta agregar el campo usuario en un cronjob de sistema. Por supuesto, si se tratase del crontab del usuario la sintaxis sería la correcta. El error en el log suele ser:

Error: bad username; while reading

Más cron...

Para mayor información acerca de cron es recomendable ir a la fuente más precisa, que son los manuales:

$ man -a cron
$ man -a crontab

Hasta el próximo artículo!


Tal vez pueda interesarte


Compartí este artículo