La lección anterior terminó con un diagnóstico y una tarea pendiente: la copia de seguridad nocturna de Tramontana se ejecuta a la hora equivocada, con la prioridad equivocada y sobre más datos de los necesarios. Alguien la programó en algún momento y nadie ha vuelto a mirarla. Es el retrato exacto de la automatización mal hecha: funciona hasta que un día tira el servicio.
cron es el temporizador de Unix. Lleva funcionando desde 1975 y sigue siendo la forma más extendida de decirle a un servidor «haz esto todos los días a las cuatro». Su sintaxis es breve y sus fallos son siempre los mismos tres, repetidos en todas las empresas del mundo. Esta lección te da la sintaxis completa y, sobre todo, esos tres fallos con su solución, porque son el 90 % de las incidencias reales de cron.
Una precisión de alcance: aquí las tareas serán one-liners o llamadas a comandos existentes. Escribir el script de copia con sus comprobaciones y su manejo de errores es el Módulo 4.
Contenido
- Qué se automatiza bien y qué se automatiza mal
- Crontab de usuario y crontab del sistema
- Los cinco campos
- El conflicto entre día del mes y día de la semana
- Fallo clásico 1: el PATH de cron
- Fallo clásico 2: la salida que nadie lee
- Fallo clásico 3: la zona horaria
- Depurar una tarea de cron
- Solapamiento y
flock anacrony los timers de systemd- Caso Tramontana: la copia nocturna y la purga
- Qué se automatiza bien y qué se automatiza mal
| Buen candidato | Mal candidato |
|---|---|
| Idempotente: repetirlo no rompe nada | Acumula efectos en cada ejecución |
| Duración acotada y predecible | Puede durar horas indefinidamente |
| Falla de forma visible y registrada | Falla en silencio |
| No necesita decisiones humanas | Requiere criterio caso por caso |
| Sus recursos están controlados | Compite libremente con el servicio |
La copia nocturna de Tramontana suspendía en las dos últimas filas: no limitaba su prioridad de E/S y nadie leía su resultado. Una tarea automática que falla en silencio es peor que no tenerla, porque genera la confianza de que el trabajo está hecho. La primera vez que descubres que las copias llevan tres meses fallando suele ser el día que necesitas restaurar una.
- Crontab de usuario y crontab del sistema
El demonio cron (paquete cron en Ubuntu) lee varios sitios distintos:
| Ubicación | Campo de usuario | Se edita con | Para qué |
|---|---|---|---|
crontab -e del usuario |
no | crontab -e |
tareas de una cuenta |
/etc/crontab |
sí | editor + sudo |
tareas del sistema |
/etc/cron.d/<fichero> |
sí | editor + sudo |
tareas de paquetes o servicios |
/etc/cron.{hourly,daily,weekly,monthly}/ |
— | scripts ejecutables | tareas periódicas simples |
operador@srv-tramontana:~$ crontab -l
30 3 * * * tar -czf /srv/tramontana/backups/envios/nocturna.tar.gz /opt/tramontanaAhí está la culpable de la incidencia de anoche.
| Opción | Efecto |
|---|---|
crontab -l |
listar |
crontab -e |
editar (usa $EDITOR, que fijaste en 03-01) |
crontab -r |
borrar el crontab entero, sin preguntar |
crontab fichero |
reemplazar el crontab por el contenido de un fichero |
crontab -u usuario -l |
ver el de otro usuario (requiere root) |
Cuidado con crontab -r. No pide confirmación y no hay papelera: borra todas tus tareas de golpe. Y está justo al lado de -e en el teclado. La costumbre profesional es sencilla: mantén tu crontab en un fichero versionado y cárgalo con crontab fichero. Así una pulsación desafortunada cuesta un crontab ~/cron/operador.cron, no una reconstrucción de memoria.
La diferencia clave entre los dos formatos es que las líneas de /etc/crontab y /etc/cron.d/ llevan un campo extra con el usuario que ejecuta la tarea, justo antes del comando:
Olvidar ese campo es un error frecuente: cron interpreta el nombre de usuario como el primer argumento del comando y la tarea falla de un modo desconcertante.
Los directorios cron.daily y compañía los ejecuta run-parts, que tiene una peculiaridad: ignora los ficheros cuyo nombre contenga un punto. Un script llamado copia.sh en /etc/cron.daily/ no se ejecutará nunca. Debe llamarse copia, sin extensión, y ser ejecutable.
- Los cinco campos
┌───── minuto (0-59) │ ┌─── hora (0-23) │ │ ┌─ día del mes (1-31) │ │ │ ┌───── mes (1-12 o jan-dec) │ │ │ │ ┌─── día sem (0-7, donde 0 y 7 son domingo, o sun-sat) │ │ │ │ │ * * * * * comando
| Sintaxis | Significa |
|---|---|
* |
cualquier valor |
5 |
ese valor exacto |
1,15,30 |
lista |
9-17 |
rango |
*/5 |
cada 5 unidades desde 0 |
0-30/10 |
cada 10, dentro del rango |
| Expresión | Cuándo se ejecuta |
|---|---|
* * * * * |
cada minuto |
*/15 * * * * |
a los minutos 0, 15, 30 y 45 |
30 4 * * * |
todos los días a las 04:30 |
0 9-18 * * 1-5 |
en punto, de 9 a 18, de lunes a viernes |
0 4 1 * * |
el día 1 de cada mes a las 04:00 |
15 2 * * 6 |
los sábados a las 02:15 |
0 0 1 1,7 * |
el 1 de enero y el 1 de julio |
*/10 2-4 * * * |
cada 10 minutos, entre las 2 y las 4 |
5 0 * * * |
a las 00:05 (no a las 00:00: evita el minuto de máxima concurrencia) |
Esa última fila es un consejo real. A las 00:00 en punto arranca todo lo que alguien programó sin pensar; desplazar cinco minutos evita competir con ello.
Atajos que sustituyen a los cinco campos:
| Atajo | Equivale a |
|---|---|
@reboot |
una vez, al arrancar el sistema |
@hourly |
0 * * * * |
@daily / @midnight |
0 0 * * * |
@weekly |
0 0 * * 0 |
@monthly |
0 0 1 * * |
@yearly |
0 0 1 1 * |
- El conflicto entre día del mes y día de la semana
Aquí hay una regla que contradice la intuición y que hace que muchas tareas se ejecuten más veces de las previstas.
Si los campos «día del mes» y «día de la semana» son ambos distintos de *, se combinan con OR, no con AND. La tarea se ejecuta si se cumple cualquiera de los dos.
La intención habitual —«solo los viernes 13»— exige comprobarlo dentro del comando, porque cron no sabe expresarla:
(Fíjate en el \%: en un crontab, el símbolo % tiene un significado especial que veremos en el apartado 6.)
Cuando uno de los dos es *, no hay ambigüedad y manda el otro. La tabla resume los cuatro casos:
| Día mes | Día semana | Resultado |
|---|---|---|
* |
* |
todos los días |
15 |
* |
solo el día 15 |
* |
1 |
solo los lunes |
15 |
1 |
los días 15 y además todos los lunes |
- Fallo clásico 1: el PATH de cron
Es, con diferencia, el número uno. El síntoma siempre es el mismo: el comando funciona perfectamente cuando lo escribes tú y no hace nada desde cron.
La causa la conoces desde 03-01: cron no lanza un shell de login ni interactivo, así que no lee /etc/profile, ni ~/.profile, ni ~/.bashrc. Nada de tu entorno existe ahí. El PATH que cron proporciona por defecto es:
Dos directorios. No están /usr/local/bin, ni /usr/sbin, ni por supuesto /home/operador/scripts que añadiste en 03-01. Tampoco existen tus alias, ni tus variables, ni la locale que tienes configurada.
Si rsync estuviera instalado en /usr/local/bin, una tarea que lo invoque por su nombre fallaría cada vez, en silencio y sin dejar rastro visible.
Las tres soluciones, por orden de preferencia:
-
Rutas absolutas siempre. Averígualas con
command -vy escríbelas tal cual.command -v rsync tar findte las da todas de una vez:/usr/bin/rsync,/usr/bin/tar,/usr/bin/find. -
Declarar el PATH en la cabecera del crontab. Las asignaciones de variables se ponen antes de las tareas y se aplican a todas:
- Hacer que el script se cargue el entorno con
. ~/.profilecomo primera línea. Funciona, pero acopla la tarea a la configuración personal de una cuenta, y eso es frágil.
La regla que aplicamos en Tramontana es la 1 reforzada con la 2: rutas absolutas en los comandos y un PATH explícito en la cabecera, para lo que el comando pueda invocar internamente.
- Fallo clásico 2: la salida que nadie lee
Cron captura stdout y stderr de cada tarea y los envía por correo local al usuario propietario. En un servidor sin MTA configurado —como srv-tramontana— ese correo no llega a ninguna parte: se descarta o se queda en /var/mail/operador sin que nadie lo abra jamás.
Resultado: si no rediriges la salida, no te enteras de nada. Ni de los éxitos ni, sobre todo, de los fallos.
| Redirección | Qué consigues |
|---|---|
| (nada) | correo local que nadie lee |
> /dev/null |
silencia la salida; los errores sí generan correo |
> /dev/null 2>&1 |
silencio absoluto. Evítalo |
>> /var/log/... 2>&1 |
lo correcto: todo queda registrado |
30 4 * * * /usr/bin/rsync -a /home/operador/datos/ /srv/tramontana/backups/envios/ >> /var/log/tramontana/cron-copia.log 2>&1
El 2>&1 va al final, por lo que aprendiste en 03-04: primero se redirige stdout al fichero y después se copia ese destino sobre stderr. Al revés, los errores —que son justo lo que te importa— acabarían en el correo fantasma.
Un registro con fecha se consigue anteponiendo un date:
30 4 * * * { /usr/bin/date '+--- %F %T inicio'; /usr/bin/rsync -a /home/operador/datos/ /srv/tramontana/backups/envios/; } >> /var/log/tramontana/cron-copia.log 2>&1MAILTO controla el destino del correo: [email protected] lo envía a una dirección real (si hay MTA), y MAILTO="" desactiva el envío por completo. La combinación profesional es MAILTO="" más redirección a un fichero de registro, y que sea un sistema de monitorización quien vigile ese fichero.
Y la peculiaridad del %: en un crontab, % se interpreta como un salto de línea que alimenta la entrada estándar del comando. Por eso date +%F dentro de un crontab hay que escribirlo date +\%F. Es una fuente de errores desproporcionada para lo raro que es el detalle.
- Fallo clásico 3: la zona horaria
cron usa la zona horaria del sistema, no la del usuario. Compruébala siempre:
En zonas con horario de verano hay dos días al año problemáticos. Cuando el reloj adelanta en marzo, las 02:30 no existen: una tarea programada a esa hora no se ejecuta ese día. Cuando atrasa en octubre, las 02:30 ocurren dos veces, y según la implementación la tarea puede ejecutarse dos veces.
Tres formas de protegerse:
- Programar fuera de la franja 01:00–03:00. Es la solución más simple y la que resuelve el problema de verdad.
- Usar UTC en el servidor (
sudo timedatectl set-timezone UTC), práctica habitual en servidores, a costa de tener que traducir mentalmente los horarios. - Declarar
CRON_TZ=UTCen la cabecera del crontab, que fija la zona solo para esas tareas.
Aquí hay una coincidencia que conviene subrayar: la copia de Tramontana estaba a las 03:30, dentro de la franja de riesgo, y además chocaba con el pico de carga. Moverla resuelve dos problemas de una vez.
- Depurar una tarea de cron
flowchart TD
A["La tarea no hace lo esperado"] --> B{"¿Se ejecutó?<br/>grep CRON /var/log/syslog"}
B -->|No| C["Revisa la sintaxis de los 5 campos<br/>y que el crontab esté cargado"]
B -->|Sí| D{"¿Hay registro<br/>en tu fichero de log?"}
D -->|No| E["Falta la redirección:<br/>añade >> log 2>&1"]
D -->|Sí| F{"¿Qué dice el error?"}
F -->|"orden no encontrada"| G["PATH: usa rutas absolutas"]
F -->|"permiso denegado"| H["Usuario equivocado<br/>o permisos del destino"]
F -->|"otro"| I["Reproduce con env -i"]
Dónde se registra. El demonio anota cada ejecución en el syslog:
operador@srv-tramontana:~$ grep CRON /var/log/syslog | tail -3
ago 18 03:30:01 srv-tramontana CRON[2210]: (operador) CMD (tar -czf /srv/tramontana/backups/envios/nocturna.tar.gz /opt/tramontana)
ago 18 04:30:01 srv-tramontana CRON[2455]: (operador) CMD (/usr/bin/rsync -a /home/operador/datos/ /srv/tramontana/backups/envios/)
ago 18 04:30:02 srv-tramontana CRON[2455]: (CRON) info (No MTA installed, discarding output)Tres cosas: la copia arrancó a las 03:30:01, coincidiendo con la incidencia; el rsync también corrió; y el mensaje «No MTA installed, discarding output» es cron diciéndote literalmente que ha tirado la salida a la basura. Ahí está el fallo 2, confesado en el propio log.
Ese registro te dice si la tarea se ejecutó, pero no si funcionó: cron no comprueba el código de salida. Para eso hace falta tu propio fichero de log.
Volcar el entorno de cron. El truco definitivo para el fallo 1: programa una tarea temporal que guarde su entorno y compáralo con el tuyo.
operador@srv-tramontana:~$ cat /tmp/entorno-cron.txt
SHELL=/bin/sh
PWD=/home/operador
LOGNAME=operador
PATH=/usr/bin:/bin
HOME=/home/operadorCinco variables. Ni LANG, ni tu PATH ampliado. Nótese además que SHELL es /bin/sh, no bash: las construcciones específicas de Bash pueden fallar. Si las necesitas, declara SHELL=/bin/bash en la cabecera.
Probar el comando con el entorno de cron, sin esperar a que se dispare:
operador@srv-tramontana:~$ env -i SHELL=/bin/sh PATH=/usr/bin:/bin HOME=/home/operador \
/bin/sh -c 'rsync -a /home/operador/datos/ /srv/tramontana/backups/envios/'
/bin/sh: 1: rsync: not foundReproducido en dos segundos. env -i arranca el comando sin ninguna variable heredada y le añade solo las que le indiques: es una simulación fiel de cron. Cualquier tarea nueva debería probarse así antes de programarla.
- Solapamiento y
flock
flockSi una ejecución dura más que el intervalo, cron lanza la siguiente igualmente. Dos copias escribiendo en el mismo fichero producen un archivo corrupto; dos purgas simultáneas pueden borrarse los pies mutuamente.
flock resuelve el problema con un fichero de bloqueo:
30 4 * * * /usr/bin/flock -n /var/lock/tramontana-copia.lock /usr/bin/tar -czf /srv/tramontana/backups/envios/nocturna.tar.gz /opt/tramontana/app/ >> /var/log/tramontana/cron-copia.log 2>&1
| Opción | Comportamiento si el bloqueo está tomado |
|---|---|
-n |
sale de inmediato con código 1 |
-w 60 |
espera hasta 60 segundos y luego se rinde |
| (nada) | espera indefinidamente |
-n es casi siempre la opción correcta para una tarea periódica: si la anterior sigue en marcha, esta sobra. Sin -n, las ejecuciones se van encolando y acabas con veinte procesos esperando.
El bloqueo se libera solo cuando el proceso termina, incluso si muere de golpe, porque lo gestiona el kernel a través del descriptor de archivo. No hay ficheros de bloqueo huérfanos que limpiar a mano, que es exactamente el problema de implementarlo con un touch y un rm.
anacron y los timers de systemd
anacron y los timers de systemdcron supone que la máquina está encendida a la hora prevista. Si el servidor estaba apagado, la tarea no se recupera. anacron cubre ese hueco: trabaja con granularidad de días y, al arrancar, ejecuta lo que quedó pendiente. En Ubuntu, cron.daily, cron.weekly y cron.monthly los gestiona anacron precisamente por eso.
| cron | anacron | systemd timer | |
|---|---|---|---|
| Granularidad | minutos | días | segundos |
| Recupera lo perdido | no | sí | sí (Persistent=true) |
| Registro | syslog | syslog | journalctl -u <unidad> |
| Dependencias entre tareas | no | no | sí |
| Aleatorizar el arranque | no | sí | RandomizedDelaySec |
| Complejidad | mínima | baja | alta (dos ficheros por tarea) |
Cuándo elegir cada uno. cron para lo simple y periódico en un servidor siempre encendido: es universal, cabe en una línea y cualquier administrador lo entiende. anacron para portátiles y máquinas que se apagan. Los timers de systemd cuando necesitas dependencias entre unidades, control de recursos, reintentos o el registro integrado del sistema; se estudian en 05-05. Para la copia de Tramontana, cron es suficiente y más legible.
- Caso Tramontana: la copia nocturna y la purga
Rehacemos la tarea culpable aplicando todo lo anterior. El crontab se mantiene en un fichero versionado y se carga desde ahí.
operador@srv-tramontana:~$ crontab -l > ~/cron-operador.bak-$(date +%F)
operador@srv-tramontana:~$ nano ~/cron/operador.cron# Crontab de operador — Tramontana Reservas SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin MAILTO="" CRON_TZ=Europe/Madrid # Copia diaria de datos y del release activo. 04:20 (fuera del pico y del cambio de hora). 20 4 * * * /usr/bin/flock -n /var/lock/tramontana-copia.lock /usr/bin/ionice -c 3 /usr/bin/tar -czf /srv/tramontana/backups/envios/datos-$(/usr/bin/date +\%F).tar.gz /home/operador/datos /opt/tramontana/app/ >> /var/log/tramontana/cron-copia.log 2>&1 # Purga de copias de más de 30 días. Domingos a las 05:10. 10 5 * * 0 /usr/bin/find /srv/tramontana/backups/envios -type f -name 'datos-*.tar.gz' -mtime +30 -delete >> /var/log/tramontana/cron-purga.log 2>&1
Se carga con crontab ~/cron/operador.cron y se comprueba con crontab -l. Repasa cada decisión, porque cada una corrige un problema concreto:
- 04:20 en lugar de 03:30: fuera del pico de errores que identificaste en 03-05 y fuera de la franja del cambio de hora.
flock -n: si una copia anterior sigue en marcha, esta no arranca.ionice -c 3: la copia solo usa el disco cuando el servicio no lo necesita. Es la corrección directa de la incidencia de 03-06./opt/tramontana/app/con barra final en vez de/opt/tramontanaentero:appes el enlace simbólico al release activo, y la barra hace quetarsiga el enlace. Se copia un release en vez de cuatro, y siempre el que está sirviendo.- Nombre con fecha:
datos-2026-08-18.tar.gz, con\%Fescapado. Cada día su fichero, en vez de sobrescribir la única copia que tenías. >> ... 2>&1en ambas tareas: todo queda registrado, con el2>&1al final.- Rutas absolutas en todos los comandos, incluido el
datede dentro de la sustitución. - La purga en tarea aparte y con
find -mtime +30: sabes de 03-03 que eso significa 31 días o más, margen sobrado para una retención mensual.
Verificación antes de irse a casa: se prueba el comando con el entorno de cron y se comprueba el resultado.
operador@srv-tramontana:~$ env -i SHELL=/bin/bash PATH=/usr/bin:/bin HOME=/home/operador /bin/bash -c \
'/usr/bin/flock -n /var/lock/tramontana-copia.lock /usr/bin/ionice -c 3 /usr/bin/tar -czf /tmp/prueba.tar.gz /home/operador/datos /opt/tramontana/app/'
operador@srv-tramontana:~$ echo $?; ls -lh /tmp/prueba.tar.gz
0
-rw-r----- 1 operador operador 47M ago 18 13:40 /tmp/prueba.tar.gz
operador@srv-tramontana:~$ tar -tzvf /tmp/prueba.tar.gz | head -2
drwxr-x--- operador/tramontana 0 2026-08-18 09:14 home/operador/datos/
-rw-r----- operador/tramontana 1284 2026-08-14 11:02 home/operador/datos/reservas.csvCódigo 0, 47 MB —un release, no cuatro— y tar -tzvf confirma que el contenido es el esperado antes de dar la tarea por buena. Es la convención del curso aplicada aquí: mirar dentro del archivo antes de confiar en él.
Informe para Marta. La copia nocturna se ha reprogramado a las 04:20, con la prioridad de disco más baja y limitada al release en servicio en lugar de a los cuatro. Esto protege frente a que la copia vuelva a dejar sin disco a la aplicación, frente a que dos copias se solapen y frente a la pérdida de la copia anterior al generarse la nueva. No protege frente a un fallo de la copia: nadie vigila todavía /var/log/tramontana/cron-copia.log, de modo que un error seguiría pasando desapercibido, y tampoco frente a la pérdida del servidor entero, porque las copias siguen en el mismo disco. Ambas cosas requieren decisiones que exceden el ajuste técnico y se abordan en el módulo de administración.
Errores Comunes y Consejos
crontab -ren lugar de-e. Borra todo sin preguntar. Mantén el crontab en un fichero y cárgalo concrontab fichero.- Olvidar el campo de usuario en
/etc/cron.d/. Cron lo toma como argumento del comando. - Poner una extensión a un script de
/etc/cron.daily/.run-partsignora los nombres con punto. - Suponer que existe tu PATH. Cron da
/usr/bin:/biny nada más. Rutas absolutas. %sin escapar. Dentro de un crontab,%es un salto de línea. Escribe\%.- No redirigir la salida. Sin
>> log 2>&1los fallos desaparecen. - Poner
2>&1antes de la redirección de salida. Va al final, siempre. - Programar entre las 02:00 y las 03:00. Es la franja del cambio de hora.
* * * * *de prueba y olvidarse de quitarlo. Deja una tarea corriendo cada minuto para siempre.- Consejo: documenta cada tarea con un comentario que diga qué hace y por qué a esa hora. Dentro de un año lo agradecerás tú, no otro.
- Consejo: haz que la tarea escriba una marca de éxito con fecha. Comprobar «¿la última línea del log es de hoy?» es un chequeo trivial y detecta el 90 % de los problemas.
Ejercicios
Ejercicio 1. Escribe las expresiones de cron para: cada 20 minutos entre las 8 y las 20 los días laborables; el último día de cada mes a las 23:50; y a las 06:15 los días 1 y 15. Justifica el segundo caso, que tiene truco.
Ejercicio 2. Programa una tarea que registre cada hora el número de errores 500 de errores.log en /var/log/tramontana/errores-500.log junto con la fecha. Aplica las tres protecciones contra los fallos clásicos y demuestra que funcionaría en el entorno de cron antes de instalarla.
Ejercicio 3. Una tarea programada */5 * * * * /home/operador/scripts/sincronizar >> /tmp/sync.log 2>&1 no produce ninguna línea en su log, aunque el comando funciona cuando lo ejecutas tú. Describe el procedimiento completo de diagnóstico, en orden, indicando qué comprobarías en cada paso y qué concluirías de cada resultado.
Soluciones
Solución 1.
*/20 8-20 * * 1-5 # cada 20 min, de 8 a 20, de lunes a viernes 15 6 1,15 * * # a las 06:15 los días 1 y 15
El segundo, «el último día de cada mes», no se puede expresar con los cinco campos: cron no sabe cuántos días tiene el mes, y 31 se saltaría febrero, abril, junio, septiembre y noviembre. La solución idiomática es programarlo todos los días y dejar que el comando decida:
Se pregunta qué día será mañana: si es día 1, hoy es el último del mes, sea cual sea su longitud y aunque el año sea bisiesto. Es un buen ejemplo de la frontera de cron: cuando la condición no cabe en los cinco campos, se ejecuta a diario y se comprueba dentro. El \% va escapado, como siempre.
Solución 2. Primero se prueba el comando en el entorno de cron:
operador@srv-tramontana:~$ env -i PATH=/usr/bin:/bin HOME=/home/operador /bin/sh -c \
'echo "$(/usr/bin/date +%F\ %T) $(/usr/bin/grep -c "ERROR 500" /var/log/tramontana/errores.log)"'
2026-08-18 13:52 41Funciona con solo /usr/bin:/bin en el PATH, luego no dependemos de nada que cron no tenga. Ahora la línea de crontab:
0 * * * * /usr/bin/flock -n /var/lock/errores-500.lock /bin/sh -c 'echo "$(/usr/bin/date +\%F\ \%T) $(/usr/bin/grep -c \"ERROR 500\" /var/log/tramontana/errores.log)"' >> /var/log/tramontana/errores-500.log 2>&1
Las tres protecciones: rutas absolutas para date y grep, de modo que el PATH mínimo no importe; >> ... 2>&1 para que tanto el resultado como cualquier error queden en el fichero y no en un correo que nadie lee; y CRON_TZ declarado en la cabecera del crontab —junto con la elección del minuto 0 de cada hora, lejos de la franja del cambio horario— para que las marcas de tiempo sean coherentes. Se añade flock porque es una costumbre barata, aunque aquí el riesgo de solapamiento sea mínimo. Y los % van escapados: sin las barras, cron cortaría el comando en el primer % y le pasaría el resto como entrada estándar, que es un fallo especialmente difícil de reconocer.
Una línea que empieza a acumular escapes como esta es la señal de que el contenido pide un script propio. Eso es lo que resolverá el Módulo 4.
Solución 3. El procedimiento, en orden, de lo más externo a lo más interno:
- ¿Existe la tarea?
crontab -l | grep sincronizar. Si no aparece, se editó otro crontab —el deroot, por ejemplo— o uncrontab -rse la llevó. - ¿Se está ejecutando?
grep CRON /var/log/syslog | grep sincronizar | tail -3. Si no hay entradas, cron no la está lanzando: revisa la sintaxis de los campos y que el demonio esté activo. Si sí las hay, cron la lanza y el problema está más adentro. - ¿Existe el fichero de log y con qué permisos?
ls -l /tmp/sync.log. Un log vacío pero existente confirma que la redirección funciona y que el comando no escribe nada; que el fichero no exista apunta a que el comando ni siquiera llegó a arrancar. - ¿Es ejecutable y con la ruta correcta?
ls -l /home/operador/scripts/sincronizar. Sin el bitx, cron obtiene «permiso denegado». Y aquí está el sospechoso principal: la tarea usa una ruta absoluta al script, pero el script por dentro probablemente invoca comandos por su nombre, y esos sí dependen del PATH. - Reproducir con el entorno de cron:
env -i PATH=/usr/bin:/bin HOME=/home/operador /bin/sh /home/operador/scripts/sincronizar. Este paso casi siempre reproduce el fallo al instante. - Verificar la escritura sobre el destino con
sudo -u operador touchen el directorio de destino, por si la tarea corre con un usuario distinto del que crees.
La conclusión más probable es la del paso 5. Y hay una pista que apunta a ello desde el principio: si el log está vacío en lugar de contener un mensaje de error, el comando no llegó a producir salida, lo que encaja con un shell que ni siquiera pudo arrancar el binario. Un log vacío es información, no ausencia de información.
Conclusión
Has convertido una tarea automática peligrosa en una tarea automática defendible.
- Sabes qué se automatiza bien: lo idempotente, acotado, con recursos controlados y con fallo visible. Y distingues el crontab de usuario del crontab del sistema, con su campo extra de usuario, y conoces
run-partsy su manía con los puntos en los nombres. - Dominas los cinco campos con listas, rangos, pasos y atajos, y sabes que día del mes y día de la semana se combinan con OR, no con AND.
- Tienes resueltos los tres fallos clásicos: el PATH mínimo de cron —rutas absolutas siempre—, la salida que nadie lee —
>> log 2>&1con el2>&1al final— y la zona horaria con su franja de riesgo entre las 02:00 y las 03:00. - Depuras una tarea con método:
grep CRON /var/log/syslogpara saber si se ejecutó, tu propio log para saber si funcionó, el volcado del entorno conenvy la reproducción exacta conenv -i. - Evitas el solapamiento con
flock -n, sabiendo que el bloqueo lo libera el kernel y no deja restos. - Conoces
anacronpara máquinas que se apagan y sabes cuándo un timer de systemd compensa su complejidad. - Y has reprogramado la copia de Tramontana con hora nueva, bloqueo, prioridad de E/S, alcance reducido al release activo, nombre con fecha y registro, con la purga en tarea aparte y un informe honesto sobre lo que la medida no cubre.
Queda un frente sin tocar. Todo lo que has hecho hasta ahora ocurre dentro de srv-tramontana, y un servidor solo existe para quien pueda llegar hasta él. La última lección del módulo, Comandos de Redes, te da las herramientas para mirar hacia fuera: ip para leer la configuración real de la máquina, ping, traceroute y mtr para el camino, dig y /etc/hosts para la resolución de nombres, ss para saber qué puertos escuchan y quién los ocupa, y curl para comprobar si el servicio responde de verdad. Todo ello ordenado en una metodología de diagnóstico por capas que aplicarás al caso que te espera: Marta avisa de que la web de reservas «no carga», y hay que averiguar exactamente dónde está el corte.
Curso de Linux: De Principiante a Administrador de Sistemas
Módulo 1: Introducción a Linux
- ¿Qué es Linux?
- Historia de Linux
- Distribuciones de Linux
- Instalando Linux
- Primer Contacto con el Sistema
- Estructura del Sistema de Archivos de Linux
Módulo 2: Comandos Básicos de Linux
- Introducción a la Línea de Comandos
- Obtener Ayuda y Documentación del Sistema
- Navegando el Sistema de Archivos
- Operaciones con Archivos y Directorios
- Visualización y Edición de Archivos
- Enlaces Duros y Simbólicos
- Permisos y Propiedad de Archivos
Módulo 3: Habilidades Avanzadas en la Línea de Comandos
- El Entorno del Shell: Variables, Alias e Historial
- Uso de Comodines y Expresiones Regulares
- Búsqueda de Archivos y Contenido: find, locate y grep
- Tuberías y Redirección
- Procesamiento de Texto: cut, sort, uniq, sed y awk
- Gestión de Procesos
- Programación de Tareas con Cron
- Comandos de Redes
Módulo 4: Scripting en Shell
- Introducción al Scripting en Shell
- Variables y Tipos de Datos
- Entrada, Salida y Argumentos de un Script
- Estructuras de Control
- Funciones y Librerías
- Depuración y Manejo de Errores
- Scripts de Producción: Buenas Prácticas
Módulo 5: Administración del Sistema
- Gestión de Usuarios y Grupos
- sudo y Permisos Especiales
- Gestión de Paquetes
- Gestión de Discos
- systemd y la Gestión de Servicios
- Registros del Sistema: journald y syslog
- Monitoreo del Sistema y Optimización del Rendimiento
- Respaldo y Restauración
Módulo 6: Redes y Seguridad
- Configuración de Redes
- SSH y Acceso Remoto
- Firewall y Seguridad Perimetral
- Sistemas de Detección de Intrusos
- Gestión de Secretos y Certificados TLS
- Asegurando Sistemas Linux
Módulo 7: Temas Avanzados
- El Proceso de Arranque y la Recuperación del Sistema
- Diagnóstico Avanzado: strace, perf y eBPF
- Optimización del Kernel de Linux
- Virtualización con Linux
- Contenedores de Linux y Docker
- Automatización con Ansible
- Alta Disponibilidad y Balanceo de Carga
