1. «En la terminal funciona»
Cuando abres una terminal, Bash lee varios archivos de configuración, como ~/.profile y ~/.bashrc, que agregan directorios al PATH, definen variables, alias y funciones, y fijan el idioma. Todo lo que ejecutas después hereda ese entorno, y uno se acostumbra a él sin notarlo. Una tarea de cron no tiene nada de eso: la lanza un servicio del sistema, sin sesión, sin terminal y sin leer ningún archivo de configuración del usuario.
Por eso la pregunta útil no es «¿por qué cron no funciona?», sino «¿de qué cosa de mi terminal depende este script sin que yo lo sepa?». Las secciones que siguen responden esa pregunta, una diferencia por vez.
2. El entorno de una tarea
La forma más directa de conocer el entorno de cron es pedírselo. Una tarea de prueba que guarda la salida de env en un archivo lo muestra tal cual:
$ (crontab -l; echo '* * * * * env > /tmp/env-cron.txt') | crontab -
# … un minuto después:
$ cat /tmp/env-cron.txt
HOME=/home/ana
LOGNAME=ana
PATH=/usr/bin:/bin
SHELL=/bin/sh
PWD=/home/ana
$ env | wc -l
47
$ crontab -l | grep -v env-cron | crontab - # quitar la tarea de prueba
Cinco variables contra cuarenta y siete. Según el sistema pueden aparecer algunas más, como LANG en los Debian que la definen en /etc/default/locale, o las que se hayan escrito al comienzo del crontab, como vimos en el tema 33. Pero nunca estará lo que agregan los archivos de configuración de tu sesión.
Comparar las dos listas muestra de un vistazo qué le falta a la tarea. comm recibe dos listas ordenadas y, con -13, muestra solo las líneas que están en la segunda y no en la primera:
$ comm -13 <(cut -d= -f1 /tmp/env-cron.txt | sort) <(env | cut -d= -f1 | sort)
DBUS_SESSION_BUS_ADDRESS
DISPLAY
EDITOR
LANG
SSH_AUTH_SOCK
TERM
USER
XDG_RUNTIME_DIR
…
3. Reproducir ese entorno
Esperar un minuto a que cron ejecute una prueba, mirar el resultado, corregir y volver a esperar es lento. Mucho más cómodo es ejecutar el comando en la terminal pero con un entorno como el de cron. Eso hace env -i: arranca un programa con el entorno vacío, salvo las variables que se le indiquen:
$ respaldar -q
Respaldo completo: 1284 archivos, 412 MiB
$ cd ~ && env -i HOME="$HOME" LOGNAME="$USER" PATH=/usr/bin:/bin SHELL=/bin/sh /bin/sh -c 'respaldar -q'
/bin/sh: 1: respaldar: not found
Ahí está el error que en cron pasaba inadvertido. La línea reproduce las tres cosas que importan: el entorno casi vacío, la shell /bin/sh en lugar de Bash, y el directorio personal como directorio de trabajo. Lo que va entre comillas simples es exactamente lo que dice el crontab después de los campos de tiempo, salvo que en el crontab los % llevan barra y aquí no.
Como es largo de escribir, conviene guardarlo como función en ~/.bashrc, el único lugar donde eso tiene sentido, porque se usa en la terminal:
# comocron 'comando' — ejecuta un comando con un entorno parecido al de cron
comocron() {
(cd ~ && env -i HOME="$HOME" LOGNAME="$USER" PATH=/usr/bin:/bin SHELL=/bin/sh /bin/sh -c "$1")
}
Los paréntesis ejecutan el cd en una subshell, para no cambiar el directorio de la terminal. El parecido no es total: cron no tiene terminal, y aquí la salida sigue viéndose en la pantalla. Pero alcanza para descubrir la gran mayoría de los problemas.
4. El PATH y las rutas absolutas
El problema más frecuente es el del ejemplo anterior: con PATH=/usr/bin:/bin, cualquier programa instalado en otro lugar no se encuentra por su nombre. Eso incluye los scripts propios de ~/bin, casi todo lo que está en /usr/local/bin, las herramientas de /usr/sbin como ip o iptables, y lo instalado con gestores como pip, npm o snap. command -v dice dónde está cada uno:
$ command -v respaldar rsync node
/home/ana/bin/respaldar
/usr/bin/rsync
/home/ana/.nvm/versions/node/v22.11.0/bin/node
Hay tres soluciones, que conviene combinar:
- Rutas absolutas en el crontab. El programa que el crontab ejecuta se escribe siempre con su ruta completa:
/home/ana/bin/respaldar -q, norespaldar -q. - Un
PATHcompleto al comienzo del crontab, como vimos en el tema 33, escrito sin variables porque cron no las expande. - Un
PATHfijo al comienzo del script. Es la más robusta, porque el script queda autosuficiente: funciona igual en cron, en la terminal, consudoo llamado desde otro script, y no depende de que nadie haya configurado nada.
#!/usr/bin/env bash
set -euo pipefail
export PATH="$HOME/bin:/usr/local/bin:/usr/bin:/bin"
# Los archivos del propio script, relativos a su ubicación y no al directorio actual
dir_script=$(dirname -- "$(readlink -f -- "$0")")
config="$dir_script/respaldo.conf"
Las dos últimas líneas resuelven el otro problema de rutas. Un script que abre respaldo.conf o ./datos/ los busca en el directorio actual, que en la terminal suele ser el del script, porque uno lo ejecuta desde allí, pero en cron es el directorio personal. Calcular las rutas a partir de la ubicación del script, con readlink -f, que resuelve los enlaces simbólicos, hace que el directorio actual deje de importar.
5. Lo que falta además del PATH
El PATH es el caso más visible, pero no el único. Estas son las otras diferencias que rompen tareas, con su síntoma típico:
| Falta | Síntoma | Solución |
|---|---|---|
Los archivos de configuración: ~/.bashrc, ~/.profile | Alias, funciones y variables propias no existen; herramientas como nvm, pyenv o conda no están activadas. | Definir en el script lo que necesita, o llamar al programa con su ruta completa. |
La terminal y TERM | tput: No value for $TERM; programas que preguntan algo o muestran barras de progreso fallan o se cuelgan. | Colores solo con [[ -t 2 ]], como en el tema 25; opciones como -q, --batch, -y. |
| La entrada estándar | Un read recibe fin de archivo y el script sigue con un valor vacío. | Ningún dato interactivo: todo por argumentos o archivos de configuración. |
El idioma (LANG) | Los nombres de días y meses salen en inglés; cambia el orden de sort; los caracteres acentuados se ven mal. | Fijar LC_ALL en el script con el idioma que se quiera. |
| El agente de SSH | ssh y rsync piden una contraseña que nadie escribe, y fallan con Permission denied. | Una clave dedicada, sin frase de paso o con permisos limitados, indicada con -i, y -o BatchMode=yes. |
sudo con contraseña | sudo: a terminal is required to read the password. | Poner la tarea en el crontab de root o en /etc/cron.d, o una regla de sudoers para ese comando. |
| La sesión gráfica | notify-send u otros programas gráficos no muestran nada. | Para avisos, mejor un correo o un mensaje a un servicio de chat. |
El idioma merece un ejemplo, porque no produce un error sino un resultado distinto, que puede pasar inadvertido mucho tiempo:
$ date '+Informe del %A %d de %B'
Informe del jueves 15 de octubre
$ comocron "date '+Informe del %A %d de %B'"
Informe del Thursday 15 de October
$ comocron "LC_ALL=es_AR.UTF-8 date '+Informe del %A %d de %B'"
Informe del jueves 15 de octubre
Una tentación frecuente es poner al comienzo del script source ~/.bashrc para «tener lo mismo que en la terminal». No funciona bien: la mayoría de los .bashrc empiezan comprobando si la shell es interactiva y, si no lo es, terminan sin hacer nada; y si no, cargan cosas pensadas para una persona, como el prompt o los alias. Es mejor que el script declare explícitamente lo que necesita, aunque sean tres líneas más.
6. Adónde va la salida
Lo que una tarea escribe, en la salida estándar o en la de errores, cron lo envía por correo al usuario, o a la dirección de MAILTO. Eso supone que el equipo tiene configurado un servicio de correo, lo que hoy es raro: en la mayoría de los servidores y equipos personales no lo hay, y entonces cron descarta la salida y deja una sola línea en su registro:
oct 15 02:30:01 servidor CRON[51201]: (ana) CMD (/home/ana/bin/respaldar -q)
oct 15 02:30:04 servidor CRON[51200]: (CRON) info (No MTA installed, discarding output)
Esa línea dice que la tarea escribió algo, quizás un mensaje de error, y que nadie lo verá. La solución es decidir adónde va la salida, redirigiéndola en el crontab:
# Las dos salidas, agregadas a un archivo
30 2 * * * /home/ana/bin/respaldar -q >> /home/ana/registros/respaldo.log 2>&1
# Las dos salidas, al registro del sistema, con una etiqueta
30 2 * * * systemd-cat -t respaldo /home/ana/bin/respaldar -q
El orden de las redirecciones importa: >> archivo 2>&1 primero manda la salida estándar al archivo y después hace que la de errores vaya al mismo lugar. Al revés, 2>&1 >> archivo, los errores se copian al destino que tenía la salida estándar antes del cambio, y siguen yendo al correo. El directorio del archivo tiene que existir: cron no lo crea, y si no está, la redirección falla y el comando ni siquiera se ejecuta.
systemd-cat envía todo al registro del sistema, que se consulta con journalctl -t respaldo, con fecha y hora en cada línea y la rotación ya resuelta. Es la versión para comandos externos del logger del tema 25. Y si el script es propio, la redirección puede ir dentro de él, con el exec >> registro 2>&1 de ese mismo tema, y la línea del crontab queda limpia.
Es muy común ver > /dev/null 2>&1 al final de las líneas de crontab, para que cron «no moleste». Funciona, pero tira también los mensajes de error, y cuando la tarea falla no queda ningún rastro de por qué. Si una tarea es demasiado conversadora, la solución es que hable menos cuando todo va bien, no taparle la boca.
7. Silencio cuando todo va bien
Hay una convención que hace que el correo de cron sea útil en lugar de molesto: una tarea no escribe nada si todo salió bien, y escribe solo cuando hay un problema. Con esa regla, cada correo de cron es una alarma que merece atención; sin ella, llegan treinta correos al día que nadie lee, y el que anuncia un fallo se pierde entre ellos.
Muchos programas tienen una opción para eso, como -q en rsync o wget. Los scripts propios pueden seguir la misma idea, con una opción -q como la del tema 25 que reduzca los mensajes a las advertencias y los errores. Y para un programa que no se puede silenciar, existe chronic, del paquete moreutils: ejecuta el comando guardando su salida, y solo la muestra si terminó con un código distinto de cero.
MAILTO=ana@example.com
30 2 * * * chronic /home/ana/bin/respaldar # un correo solo si falla
El script de la sección 10 aplica esta misma idea, y agrega lo que chronic no hace: guardar también la salida de las ejecuciones exitosas, por si alguna vez hace falta consultarla.
8. Revisar qué pasó
Cuando una tarea no hizo lo que debía, conviene revisar en orden, desde lo más general a lo más particular, en lugar de cambiar cosas al azar. Es el método del tema 26 aplicado a cron:
- ¿Está cron en marcha?
systemctl status cron, ocronden Fedora y RHEL. En contenedores y en algunos sistemas mínimos, cron ni siquiera está instalado. sistema - ¿Está la línea en el crontab correcto?
crontab -lmuestra el del usuario actual;sudo crontab -l, el de root, que es otro. Una confusión habitual es instalar la tarea en uno y buscarla en el otro. instalada - ¿Cron la lanzó? En el registro de cron tiene que haber una línea
CMDa la hora prevista. Si no la hay, el problema está en el horario o en el archivo: un campo mal escrito, un%que cortó la línea, un archivo de/etc/cron.dignorado. lanzada - ¿Qué dijo? Su registro, su correo, o la línea
No MTA installedque indica que dijo algo y se perdió. salida - ¿Falla en el entorno de cron? Ejecutar la línea con
comocron, y si falla allí, se depura en la terminal como cualquier script, conbash -x. reproducir
$ journalctl -u cron --since today | grep -v pam_unix
oct 15 02:30:01 servidor CRON[51201]: (ana) CMD (/home/ana/bin/respaldar -q)
oct 15 02:30:04 servidor CRON[51200]: (CRON) info (No MTA installed, discarding output)
oct 15 03:00:01 servidor cron[612]: (*system*respaldo) WRONG FILE OWNER (/etc/cron.d/respaldo)
$ grep CRON /var/log/syslog # en sistemas sin journald
El grep -v quita las líneas de apertura y cierre de sesión, que aparecen en cada ejecución y no aportan nada aquí. La última línea muestra otro tipo de mensaje: cron rechazó un archivo de /etc/cron.d porque no pertenece a root. Es una de las pocas ocasiones en que cron avisa de un problema con un archivo; por eso conviene mirar su registro después de instalar uno.
Lo que el registro de cron no dice es cómo terminó la tarea: anota que la lanzó, pero no su código de salida ni cuánto tardó. Para saberlo, la tarea tiene que registrarlo ella misma.
9. Registrar cada ejecución
Tres preguntas aparecen siempre que se administra un conjunto de tareas programadas: ¿cuándo corrió por última vez?, ¿terminó bien?, ¿qué dijo? Si cada script responde a su manera, o no responde, las respuestas están dispersas o no existen. La solución habitual es un envoltorio: un script que recibe la tarea como argumento, la ejecuta y se ocupa de todo lo que la rodea.
MAILTO=ana@example.com
30 2 * * * /home/ana/bin/ejecutar-tarea -t 3600 respaldo /home/ana/bin/respaldar
*/10 * * * * /home/ana/bin/ejecutar-tarea -t 540 sincronizar /home/ana/bin/sincronizar
0 8 * * 1 /home/ana/bin/ejecutar-tarea informe /home/ana/bin/informe-semanal
Las tareas no necesitan saber nada de cron, y todas ganan lo mismo: un entorno conocido, un bloqueo contra ejecuciones simultáneas, un límite de tiempo, un registro con la fecha, el código y la duración de cada ejecución, y un correo solo cuando algo falla. Eso es lo que hace el script de la sección siguiente.
10. Un script completo
ejecutar-tarea reúne todo el tema y varias técnicas de los bloques anteriores: el modo estricto, getopts, flock, mktemp con trap, y los códigos de salida para distinguir los fallos.
#!/usr/bin/env bash
#
# ejecutar-tarea — envuelve una tarea de cron: le da un entorno conocido,
# evita ejecuciones simultáneas, registra cada ejecución y solo produce
# salida, y por lo tanto correo, cuando la tarea falla.
#
# Uso: ejecutar-tarea [-t SEGUNDOS] NOMBRE COMANDO [ARGUMENTO...]
# -t límite de tiempo; si se supera, la tarea se detiene
#
set -euo pipefail
export PATH="$HOME/bin:/usr/local/bin:/usr/bin:/bin"
export LC_ALL=C.UTF-8
umask 027
prog="${0##*/}"
uso() { echo "Uso: $prog [-t SEGUNDOS] NOMBRE COMANDO [ARGUMENTO...]" >&2; exit 2; }
ahora() { date '+%F %T'; }
limite=0
while getopts ":t:" opcion; do
case "$opcion" in
t) [[ "$OPTARG" =~ ^[1-9][0-9]*$ ]] || uso; limite="$OPTARG" ;;
*) uso ;;
esac
done
shift $(( OPTIND - 1 ))
(( $# >= 2 )) || uso
nombre="$1"; shift
[[ "$nombre" =~ ^[A-Za-z0-9_-]+$ ]] || { echo "$prog: nombre no válido: $nombre" >&2; exit 2; }
dir="${XDG_STATE_HOME:-$HOME/.local/state}/tareas"
mkdir -p "$dir"
registro="$dir/$nombre.log"
exec 9> "$dir/$nombre.lock"
if ! flock -n 9; then
echo "$(ahora) ya hay una ejecución en marcha; esta se omite" >> "$registro"
exit 0
fi
if [[ -f "$registro" ]] && (( $(stat -c %s "$registro") > 1048576 )); then
mv -f -- "$registro" "$registro.1" # más de 1 MiB: se guarda uno anterior
fi
salida=$(mktemp)
trap 'rm -f -- "$salida"' EXIT
cd "$HOME"
echo "$(ahora) inicio: $*" >> "$registro"
inicio=$SECONDS
codigo=0
if (( limite > 0 )); then
timeout "$limite" "$@" < /dev/null > "$salida" 2>&1 || codigo=$?
else
"$@" < /dev/null > "$salida" 2>&1 || codigo=$?
fi
duracion=$(( SECONDS - inicio ))
cat -- "$salida" >> "$registro"
echo "$(ahora) fin: código $codigo, $duracion s" >> "$registro"
echo "$(ahora) $codigo $duracion" > "$dir/$nombre.ultimo"
if (( codigo != 0 )); then
motivo="terminó con código $codigo"
(( limite > 0 && codigo == 124 )) && motivo="superó el límite de $limite s"
logger -t "$prog" -p user.err -- "$nombre: $motivo" || true
echo "La tarea $nombre $motivo. Su salida fue:" # esto lo envía cron por correo
cat -- "$salida"
fi
exit "$codigo"
$ ejecutar-tarea respaldo /home/ana/bin/respaldar
$ echo $?
0
$ ejecutar-tarea sincronizar /home/ana/bin/sincronizar
La tarea sincronizar terminó con código 3. Su salida fue:
Conectando con copias.example.com…
sincronizar: no se pudo conectar con copias.example.com
$ ejecutar-tarea -t 2 lenta sleep 5
La tarea lenta superó el límite de 2 s. Su salida fue:
$ ejecutar-tarea informe informe-semanal
La tarea informe terminó con código 127. Su salida fue:
/home/ana/bin/ejecutar-tarea: line 56: informe-semanal: command not found
$ cat ~/.local/state/tareas/respaldo.log
2026-10-15 02:30:01 inicio: /home/ana/bin/respaldar
Copiando /home/ana/documentos…
Respaldo completo: 1284 archivos, 412 MiB
2026-10-15 02:31:47 fin: código 0, 106 s
$ cat ~/.local/state/tareas/sincronizar.ultimo
2026-10-15 10:40:02 3 1
$ journalctl -t ejecutar-tarea --since today
oct 15 10:40:02 servidor ejecutar-tarea[52817]: sincronizar: terminó con código 3
oct 15 10:41:12 servidor ejecutar-tarea[52866]: lenta: superó el límite de 2 s
oct 15 10:41:30 servidor ejecutar-tarea[52890]: informe: terminó con código 127
- Un entorno conocido: el script fija su propio
PATH, el idioma conLC_ALLy el directorio de trabajo, de modo que la tarea se comporta igual en cron que en la terminal.C.UTF-8da fechas y números predecibles sin estropear los acentos; si los informes deben salir en castellano, basta con cambiarlo.umask 027hace que los archivos que cree la tarea no los pueda leer cualquier usuario del sistema. - Sin entrada: la tarea recibe
< /dev/null, de modo que un programa que pregunte algo recibe fin de archivo de inmediato, tanto en cron como al probarla a mano, en lugar de quedarse esperando. - Un bloqueo por tarea: con el
flock -ndel tema 27, si la ejecución anterior desincronizarsigue en marcha, la nueva lo anota en el registro y termina con 0. No es un error, sino algo esperable, y no tiene por qué generar un correo. - Un límite de tiempo:
timeoutdetiene la tarea si supera los segundos indicados, y termina con el código 124, como vimos en el tema 15. Así, una conexión colgada no bloquea para siempre las ejecuciones siguientes. El límite desincronizar, 540 segundos, queda por debajo de su intervalo de 10 minutos. - La salida se guarda siempre y se muestra solo si falla: va a un temporal, que el
trapdel tema 24 borra al terminar, y de allí al registro. Solo cuando el código no es cero se copia a la salida estándar, que cron envía por correo; además, el fallo queda en el registro del sistema conlogger, por si el correo no existe. - Dos archivos por tarea:
NOMBRE.log, con la historia completa, que se rota de forma simple al pasar de 1 MiB; yNOMBRE.ultimo, con una sola línea, la fecha, el código y la duración de la última ejecución, fácil de leer desde otro script. Se guardan en~/.local/state, el lugar que el estándar XDG reserva para estos datos. - El código de salida se conserva: el envoltorio termina con el mismo código que la tarea, de modo que también puede usarse desde otros scripts, donde ese código sí importa.
Con LC_ALL fijado, los mensajes de los programas salen en inglés, como el command not found del ejemplo, que es además lo que conviene para buscar un error en Internet. Y una limitación: el bloqueo lo hereda la tarea, de modo que si esta lanza un proceso en segundo plano que sigue vivo después de terminar, el bloqueo dura lo que dure ese proceso.
11. Errores frecuentes
| Escribiste | Qué pasa | Corrección |
|---|---|---|
30 2 * * * respaldar | respaldar: not found: ~/bin no está en el PATH de cron. | La ruta absoluta, y un PATH fijo en el script. |
Un script que abre ./config.conf | En cron, el directorio actual es el personal: no lo encuentra. | Rutas a partir de dirname "$(readlink -f "$0")". |
source ~/.bashrc al comienzo del script | No carga nada, porque la shell no es interactiva, o carga cosas inútiles. | Declarar en el script lo que necesita. |
... > /dev/null 2>&1 en el crontab | Los errores se pierden, y cuando falla no hay rastro. | Redirigir a un archivo o al registro del sistema. |
... 2>&1 >> archivo | Los errores siguen yendo al correo, o se pierden. | >> archivo 2>&1 |
>> ~/registros/x.log sin crear el directorio | La redirección falla y la tarea no se ejecuta. | Crear el directorio antes, o redirigir dentro del script. |
| Una tarea que escribe en cada ejecución | Muchos correos que nadie lee; el del fallo se pierde. | Silencio salvo error: -q, chronic o un envoltorio. |
Fechas con date +%A en un informe | Sin idioma, salen en inglés. | export LC_ALL=es_AR.UTF-8 en el script. |
rsync o ssh con la clave del agente | En cron no hay agente: Permission denied, o se queda esperando. | -i clave y -o BatchMode=yes. |
sudo dentro de la tarea | a terminal is required to read the password. | El crontab de root, /etc/cron.d, o una regla de sudoers. |
Buscar la tarea con crontab -l siendo otro usuario | No aparece: está en el crontab de root, o al revés. | sudo crontab -l, sudo crontab -u usuario -l. |
12. Resumen
- Una tarea de cron no lee
~/.profileni~/.bashrc, no tiene terminal y recibe un puñado de variables, conPATH=/usr/bin:/binySHELL=/bin/sh. - El entorno real se ve con una tarea de prueba que ejecuta
env, y se reproduce en la terminal conenv -i … /bin/sh -c 'comando'desde el directorio personal. - El problema más común es el
PATH: rutas absolutas en el crontab y unPATHfijo al comienzo del script lo resuelven. Los archivos propios del script se ubican a partir de su ruta, no del directorio actual. - También faltan la entrada y la terminal, el idioma, el agente de SSH, la contraseña de
sudoy la sesión gráfica; cada una tiene su síntoma y su solución. - La salida va por correo, y sin servicio de correo se pierde: el registro de cron lo indica con
No MTA installed, discarding output. >> archivo 2>&1, en ese orden, guarda las dos salidas;systemd-cat -t etiquetalas manda al registro del sistema. Descartarlas con/dev/nullborra también los errores.- Una tarea bien diseñada calla cuando todo va bien y habla solo cuando falla;
chroniclo consigue con cualquier programa. - Para revisar un fallo: ¿cron en marcha?, ¿línea en el crontab correcto?, ¿línea
CMDen el registro?, ¿qué dijo la tarea?, ¿falla también en el entorno de cron? - Cron no registra cómo terminó una tarea; un envoltorio como
ejecutar-tareaagrega un entorno fijo, bloqueo, límite de tiempo, registro y aviso solo en caso de error.
13. Preguntas de repaso
Intenta responder cada una antes de abrir la respuesta.
1. ¿Por qué un programa instalado en ~/bin se encuentra en la terminal y no en cron?
Porque ~/bin lo agrega al PATH algún archivo de configuración de la sesión, como ~/.profile, que cron no lee. En cron, el PATH es solo /usr/bin:/bin.
2. ¿Cómo se averigua qué variables recibe exactamente una tarea de cron?
Con una tarea de prueba que guarde la salida de env en un archivo, como * * * * * env > /tmp/env-cron.txt, y quitándola después de revisar el archivo.
3. ¿Qué hace env -i HOME="$HOME" PATH=/usr/bin:/bin /bin/sh -c 'comando'?
Ejecuta el comando con /bin/sh y un entorno vacío salvo esas dos variables, parecido al de cron. Sirve para reproducir en la terminal los errores que aparecen en cron, sin esperar a que se ejecute la tarea.
4. Un script abre datos.csv, que está junto a él. Funciona a mano, pero no en cron. ¿Por qué, y cómo se corrige?
Porque el nombre es relativo al directorio actual, que a mano suele ser el del script y en cron es el directorio personal. Se corrige calculando la ruta desde la ubicación del script: "$(dirname -- "$(readlink -f -- "$0")")/datos.csv".
5. ¿Qué significa la línea No MTA installed, discarding output en el registro de cron?
Que la tarea escribió algo en su salida, que cron quiso enviar por correo, pero el equipo no tiene servicio de correo, y la salida se descartó. Puede haber sido un mensaje de error.
6. ¿Qué diferencia hay entre >> registro 2>&1 y 2>&1 >> registro?
En la primera, las dos salidas van al archivo. En la segunda, la de errores se copia al destino que tenía la salida estándar antes de redirigirla, es decir, el original de cron, y solo la salida estándar va al archivo.
7. ¿Por qué no conviene terminar las líneas de crontab con > /dev/null 2>&1?
Porque descarta también los mensajes de error: cuando la tarea falla, no queda ningún rastro de por qué. Es mejor guardar la salida en un registro y que la tarea calle cuando todo va bien.
8. ¿Qué hace chronic?
Ejecuta un comando guardando su salida, y solo la muestra si el comando terminó con un código distinto de cero. En cron, eso hace que llegue un correo solo cuando la tarea falla.
9. El registro de cron muestra la línea CMD de una tarea a la hora correcta. ¿Qué dice eso, y qué no?
Dice que cron lanzó el comando, de modo que el horario y la línea están bien. No dice si el comando funcionó: para eso hay que mirar su salida, su registro o su resultado.
10. ¿Por qué ejecutar-tarea termina con 0 cuando encuentra el bloqueo ocupado?
Porque que la ejecución anterior siga en marcha no es un fallo sino una situación prevista: la nueva se omite y lo anota en el registro. Terminar con 0 y sin escribir nada en la salida evita un correo innecesario.
Para practicar
- Guarda el entorno de cron con una tarea de prueba y compáralo con el de tu terminal usando
comm. ¿Cuántas variables faltan? - Agrega la función
comocrona tu~/.bashrcy úsala para probar los comandos de tu crontab. ¿Alguno falla? - Escribe un script que use un programa de
~/biny abra un archivo junto a él. Hazlo fallar en cron y luego corrígelo con unPATHfijo y rutas calculadas desde$0. - Programa una tarea que escriba la fecha con
date +\%Aen un archivo, y comprueba en qué idioma sale. Luego fijaLC_ALL. - Instala
ejecutar-tareay usa con él una tarea que a veces falle, como unping -c1a un equipo que no siempre está encendido. Revisa el.log, el.ultimoyjournalctl -t ejecutar-tarea. - Escribe un script
estado-tareasque lea todos los archivos.ultimoy muestre una tabla con el nombre, la fecha, el resultado y la duración de cada tarea, marcando las que fallaron o hace más de un día que no corren.