Automatiza tareas del sistema con cron y systemd para administración eficiente
La programación de tareas es esencial para la administración automatizada de sistemas. Cron permite ejecutar scripts y comandos en horarios específicos, mientras que systemd ofrece capacidades avanzadas de scheduling y gestión de servicios.
Formato: minuto hora día mes día_semana comando
Expresión Cron | Descripción | Frecuencia |
---|---|---|
0 2 * * * |
Diariamente a las 2:00 AM | Una vez al día |
*/5 * * * * |
Cada 5 minutos | 12 veces por hora |
0 0 * * 1 |
Lunes a medianoche | Una vez por semana |
30 14 1 * * |
Primer día del mes a las 2:30 PM | Una vez por mes |
0 */4 * * * |
Cada 4 horas | 6 veces al día |
0 9-17 * * 1-5 |
Cada hora de 9AM a 5PM, lunes a viernes | Horario laboral |
@reboot |
Al iniciar el sistema | Una vez por reinicio |
@daily |
Equivalente a 0 0 * * * |
Una vez al día |
#!/bin/bash
# Gestor avanzado de crontab con respaldos y validación
# Versión: 2.0
set -euo pipefail
# Configuración
declare -g BACKUP_DIR="/var/backups/crontab"
declare -g LOG_FILE="/var/log/cron_manager.log"
declare -g CONFIG_DIR="$HOME/.cron_manager"
declare -g TEMPLATES_DIR="$CONFIG_DIR/templates"
# Crear directorios necesarios
mkdir -p "$BACKUP_DIR" "$CONFIG_DIR" "$TEMPLATES_DIR"
# Función de logging
log() {
local message="[$(date '+%Y-%m-%d %H:%M:%S')] $*"
echo "$message" | tee -a "$LOG_FILE"
}
# Crear backup del crontab actual
backup_crontab() {
local user="${1:-$(whoami)}"
local backup_file="$BACKUP_DIR/crontab_${user}_$(date +%Y%m%d_%H%M%S).bak"
log "Creando backup del crontab de $user"
if crontab -l -u "$user" >/dev/null 2>&1; then
crontab -l -u "$user" > "$backup_file"
log "Backup creado: $backup_file"
echo "$backup_file"
else
log "No hay crontab existente para el usuario $user"
touch "$backup_file"
echo "$backup_file"
fi
}
# Validar expresión cron
validate_cron_expression() {
local expression="$1"
# Separar la expresión en campos
IFS=' ' read -ra FIELDS <<< "$expression"
# Verificar que tenga 5 o 6 campos (6 si incluye segundos en algunas implementaciones)
if [ ${#FIELDS[@]} -lt 5 ]; then
echo "Error: Expresión cron incompleta. Se requieren al menos 5 campos."
return 1
fi
# Extraer campos
local minute="${FIELDS[0]}"
local hour="${FIELDS[1]}"
local day="${FIELDS[2]}"
local month="${FIELDS[3]}"
local weekday="${FIELDS[4]}"
# Función auxiliar para validar rangos
validate_field() {
local field="$1"
local min_val="$2"
local max_val="$3"
local field_name="$4"
# Permitir asterisco y expresiones especiales
if [[ "$field" == "*" ]] || [[ "$field" =~ ^@(reboot|yearly|annually|monthly|weekly|daily|hourly)$ ]]; then
return 0
fi
# Validar rangos con */step
if [[ "$field" =~ ^\*/[0-9]+$ ]]; then
local step=${field#*/}
if [ "$step" -gt "$max_val" ]; then
echo "Error: Paso demasiado grande para $field_name: $step"
return 1
fi
return 0
fi
# Validar rangos simples
if [[ "$field" =~ ^[0-9]+$ ]]; then
if [ "$field" -lt "$min_val" ] || [ "$field" -gt "$max_val" ]; then
echo "Error: Valor fuera de rango para $field_name: $field (rango: $min_val-$max_val)"
return 1
fi
return 0
fi
# Validar listas (ej: 1,3,5)
if [[ "$field" =~ ^[0-9,]+$ ]]; then
IFS=',' read -ra VALUES <<< "$field"
for value in "${VALUES[@]}"; do
if [ "$value" -lt "$min_val" ] || [ "$value" -gt "$max_val" ]; then
echo "Error: Valor fuera de rango en $field_name: $value"
return 1
fi
done
return 0
fi
# Validar rangos (ej: 9-17)
if [[ "$field" =~ ^[0-9]+-[0-9]+$ ]]; then
local start=${field%-*}
local end=${field#*-}
if [ "$start" -lt "$min_val" ] || [ "$start" -gt "$max_val" ] || \
[ "$end" -lt "$min_val" ] || [ "$end" -gt "$max_val" ] || \
[ "$start" -gt "$end" ]; then
echo "Error: Rango inválido para $field_name: $field"
return 1
fi
return 0
fi
echo "Error: Formato inválido para $field_name: $field"
return 1
}
# Manejar expresiones especiales
if [[ "$minute" =~ ^@(reboot|yearly|annually|monthly|weekly|daily|hourly)$ ]]; then
return 0
fi
# Validar cada campo
validate_field "$minute" 0 59 "minuto" || return 1
validate_field "$hour" 0 23 "hora" || return 1
validate_field "$day" 1 31 "día" || return 1
validate_field "$month" 1 12 "mes" || return 1
validate_field "$weekday" 0 7 "día de la semana" || return 1
return 0
}
# Agregar tarea cron
add_cron_job() {
local user="${1:-$(whoami)}"
local schedule="$2"
local command="$3"
local description="${4:-Tarea automatizada}"
log "Agregando tarea cron para usuario $user"
log "Horario: $schedule"
log "Comando: $command"
# Validar expresión cron
if ! validate_cron_expression "$schedule"; then
return 1
fi
# Crear backup
local backup_file
backup_file=$(backup_crontab "$user")
# Obtener crontab actual
local current_crontab=""
if crontab -l -u "$user" >/dev/null 2>&1; then
current_crontab=$(crontab -l -u "$user")
fi
# Agregar nueva tarea con comentario
local new_entry="# $description - Agregado $(date)
$schedule $command"
# Verificar si la tarea ya existe
if echo "$current_crontab" | grep -Fq "$schedule $command"; then
log "Advertencia: La tarea ya existe en el crontab"
return 2
fi
# Crear nuevo crontab
local new_crontab=""
if [ -n "$current_crontab" ]; then
new_crontab="$current_crontab
$new_entry"
else
new_crontab="$new_entry"
fi
# Aplicar nuevo crontab
echo "$new_crontab" | crontab -u "$user" -
if [ $? -eq 0 ]; then
log "✓ Tarea agregada exitosamente"
return 0
else
log "✗ Error al agregar tarea, restaurando backup"
if [ -s "$backup_file" ]; then
crontab -u "$user" "$backup_file"
fi
return 1
fi
}
# Eliminar tarea cron
remove_cron_job() {
local user="${1:-$(whoami)}"
local pattern="$2"
log "Eliminando tareas que coincidan con: $pattern"
# Crear backup
local backup_file
backup_file=$(backup_crontab "$user")
# Obtener crontab actual
if ! crontab -l -u "$user" >/dev/null 2>&1; then
log "No hay crontab para el usuario $user"
return 1
fi
local current_crontab
current_crontab=$(crontab -l -u "$user")
# Filtrar líneas que no coincidan con el patrón
local new_crontab
new_crontab=$(echo "$current_crontab" | grep -v "$pattern")
# Verificar si se eliminó algo
if [ "$current_crontab" = "$new_crontab" ]; then
log "No se encontraron tareas que coincidan con el patrón"
return 2
fi
# Aplicar nuevo crontab
if [ -n "$new_crontab" ]; then
echo "$new_crontab" | crontab -u "$user" -
else
crontab -r -u "$user" # Eliminar todo el crontab si está vacío
fi
if [ $? -eq 0 ]; then
log "✓ Tareas eliminadas exitosamente"
return 0
else
log "✗ Error al eliminar tareas, restaurando backup"
crontab -u "$user" "$backup_file"
return 1
fi
}
# Listar tareas cron con formato mejorado
list_cron_jobs() {
local user="${1:-$(whoami)}"
echo "=== TAREAS CRON PARA $user ==="
echo ""
if ! crontab -l -u "$user" >/dev/null 2>&1; then
echo "No hay tareas programadas para el usuario $user"
return 1
fi
local crontab_content
crontab_content=$(crontab -l -u "$user")
echo "$crontab_content" | while IFS= read -r line; do
if [[ "$line" =~ ^#.* ]]; then
# Línea de comentario
echo -e "\033[36m$line\033[0m"
elif [[ -n "$line" ]]; then
# Línea de tarea cron
echo -e "\033[32m$line\033[0m"
# Intentar explicar la programación
local schedule=$(echo "$line" | awk '{print $1" "$2" "$3" "$4" "$5}')
local explanation
explanation=$(explain_cron_schedule "$schedule")
if [ -n "$explanation" ]; then
echo -e "\033[90m → $explanation\033[0m"
fi
echo ""
fi
done
}
# Explicar horario cron en lenguaje natural
explain_cron_schedule() {
local schedule="$1"
IFS=' ' read -ra FIELDS <<< "$schedule"
if [ ${#FIELDS[@]} -ne 5 ]; then
return 1
fi
local minute="${FIELDS[0]}"
local hour="${FIELDS[1]}"
local day="${FIELDS[2]}"
local month="${FIELDS[3]}"
local weekday="${FIELDS[4]}"
local explanation=""
# Casos especiales
case "$schedule" in
"0 0 * * *") explanation="Diariamente a medianoche" ;;
"0 2 * * *") explanation="Diariamente a las 2:00 AM" ;;
"*/5 * * * *") explanation="Cada 5 minutos" ;;
"*/15 * * * *") explanation="Cada 15 minutos" ;;
"*/30 * * * *") explanation="Cada 30 minutos" ;;
"0 * * * *") explanation="Cada hora" ;;
"0 0 * * 0") explanation="Semanalmente los domingos a medianoche" ;;
"0 0 1 * *") explanation="Mensualmente el día 1 a medianoche" ;;
*)
# Construir explicación dinámica
local freq_parts=()
# Minuto
case "$minute" in
"*") ;;
"0") freq_parts+=("a las :00") ;;
"*/5") freq_parts+=("cada 5 minutos") ;;
"*/15") freq_parts+=("cada 15 minutos") ;;
"*/30") freq_parts+=("cada 30 minutos") ;;
[0-9]*) freq_parts+=("en el minuto $minute") ;;
esac
# Hora
case "$hour" in
"*") ;;
"*/2") freq_parts+=("cada 2 horas") ;;
"*/4") freq_parts+=("cada 4 horas") ;;
[0-9]*) freq_parts+=("a las ${hour}:00") ;;
esac
# Día del mes
case "$day" in
"*") ;;
"1") freq_parts+=("el día 1 del mes") ;;
[0-9]*) freq_parts+=("el día $day del mes") ;;
esac
# Día de la semana
case "$weekday" in
"*") ;;
"0"|"7") freq_parts+=("los domingos") ;;
"1") freq_parts+=("los lunes") ;;
"2") freq_parts+=("los martes") ;;
"3") freq_parts+=("los miércoles") ;;
"4") freq_parts+=("los jueves") ;;
"5") freq_parts+=("los viernes") ;;
"6") freq_parts+=("los sábados") ;;
"1-5") freq_parts+=("de lunes a viernes") ;;
esac
if [ ${#freq_parts[@]} -gt 0 ]; then
explanation=$(IFS=", "; echo "${freq_parts[*]}")
explanation="Se ejecuta $explanation"
fi
;;
esac
echo "$explanation"
}
# Crear plantillas de tareas comunes
create_templates() {
local templates=(
"backup_daily:0 2 * * *:/usr/local/bin/backup_script.sh:Backup diario"
"backup_weekly:0 3 * * 1:/usr/local/bin/backup_weekly.sh:Backup semanal"
"cleanup_logs:0 4 * * *:/usr/local/bin/cleanup_logs.sh:Limpieza diaria de logs"
"update_system:0 5 * * 1:/usr/bin/apt update && /usr/bin/apt upgrade -y:Actualización semanal del sistema"
"monitor_disk:*/30 * * * *:/usr/local/bin/check_disk_space.sh:Monitoreo de espacio en disco"
"restart_service:0 6 * * *:systemctl restart nginx:Reiniciar nginx diariamente"
"generate_report:0 8 1 * *:/usr/local/bin/monthly_report.sh:Reporte mensual"
"check_ssl:0 9 * * 1:/usr/local/bin/check_ssl_certs.sh:Verificar certificados SSL"
)
log "Creando plantillas de tareas cron"
for template in "${templates[@]}"; do
IFS=':' read -ra TEMPLATE_PARTS <<< "$template"
local name="${TEMPLATE_PARTS[0]}"
local schedule="${TEMPLATE_PARTS[1]}"
local command="${TEMPLATE_PARTS[2]}"
local description="${TEMPLATE_PARTS[3]}"
cat > "$TEMPLATES_DIR/$name.template" << EOF
# Plantilla: $name
# Descripción: $description
# Programación: $schedule
# Comando: $command
$schedule $command
EOF
done
log "Plantillas creadas en $TEMPLATES_DIR"
}
# Aplicar plantilla
apply_template() {
local user="${1:-$(whoami)}"
local template_name="$2"
local template_file="$TEMPLATES_DIR/$template_name.template"
if [ ! -f "$template_file" ]; then
log "Error: Plantilla no encontrada: $template_name"
return 1
fi
log "Aplicando plantilla: $template_name"
# Leer plantilla (ignorar líneas de comentario)
local cron_line
cron_line=$(grep -v '^#' "$template_file" | head -1)
if [ -n "$cron_line" ]; then
# Extraer partes de la línea cron
local schedule command
schedule=$(echo "$cron_line" | awk '{print $1" "$2" "$3" "$4" "$5}')
command=$(echo "$cron_line" | cut -d' ' -f6-)
# Obtener descripción del template
local description
description=$(grep "^# Descripción:" "$template_file" | cut -d' ' -f3-)
add_cron_job "$user" "$schedule" "$command" "$description"
else
log "Error: Plantilla vacía o inválida"
return 1
fi
}
# Monitorear ejecución de cron
monitor_cron_execution() {
local duration="${1:-300}" # 5 minutos por defecto
log "Monitoreando ejecuciones de cron por $duration segundos"
# Seguir el log de cron
timeout "$duration" tail -f /var/log/cron 2>/dev/null || \
timeout "$duration" tail -f /var/log/syslog | grep CRON 2>/dev/null || \
{
echo "No se pudo acceder a los logs de cron"
echo "Intente con: sudo journalctl -f -u cron"
}
}
# Generar reporte de tareas cron
generate_cron_report() {
local output_file="${1:-/tmp/cron_report_$(date +%Y%m%d_%H%M%S).html}"
log "Generando reporte de tareas cron: $output_file"
cat > "$output_file" << 'EOF'
Reporte de Tareas Cron
Reporte de Tareas Cron
Generado el: $(date)
Servidor: $(hostname)
EOF
# Obtener todos los usuarios con crontab
local users=()
# Usuarios del sistema
for user in $(cut -f1 -d: /etc/passwd); do
if crontab -l -u "$user" >/dev/null 2>&1; then
users+=("$user")
fi
done
# Usuario actual si no está en la lista
if [[ ! " ${users[*]} " =~ " $(whoami) " ]] && crontab -l >/dev/null 2>&1; then
users+=("$(whoami)")
fi
echo "Usuarios con Tareas Programadas: ${#users[@]}
" >> "$output_file"
for user in "${users[@]}"; do
echo "" >> "$output_file"
echo "Usuario: $user
" >> "$output_file"
local crontab_content
crontab_content=$(crontab -l -u "$user" 2>/dev/null)
echo "" >> "$output_file"
echo "Programación Comando Explicación " >> "$output_file"
echo "$crontab_content" | while IFS= read -r line; do
if [[ ! "$line" =~ ^#.* ]] && [[ -n "$line" ]]; then
local schedule command explanation
schedule=$(echo "$line" | awk '{print $1" "$2" "$3" "$4" "$5}')
command=$(echo "$line" | cut -d' ' -f6-)
explanation=$(explain_cron_schedule "$schedule")
echo "" >> "$output_file"
echo "$schedule " >> "$output_file"
echo "$command " >> "$output_file"
echo "${explanation:-'N/A'} " >> "$output_file"
echo " " >> "$output_file"
fi
done
echo "
" >> "$output_file"
echo "" >> "$output_file"
done
echo "" >> "$output_file"
log "Reporte generado: $output_file"
echo "$output_file"
}
# Función de ayuda
show_help() {
cat << EOF
Gestor Avanzado de Crontab v2.0
USO:
$0 COMANDO [OPCIONES]
COMANDOS:
add USER SCHEDULE COMMAND [DESCRIPTION]
Agregar nueva tarea cron
remove USER PATTERN
Eliminar tareas que coincidan con el patrón
list [USER]
Listar tareas del usuario
validate "CRON_EXPRESSION"
Validar expresión cron
explain "CRON_EXPRESSION"
Explicar horario en lenguaje natural
backup [USER]
Crear backup del crontab
templates
Crear plantillas de tareas comunes
apply USER TEMPLATE_NAME
Aplicar plantilla
monitor [SECONDS]
Monitorear ejecuciones de cron
report [OUTPUT_FILE]
Generar reporte HTML
help
Mostrar esta ayuda
EJEMPLOS:
# Agregar backup diario
$0 add root "0 2 * * *" "/usr/local/bin/backup.sh" "Backup diario"
# Listar tareas del usuario actual
$0 list
# Validar expresión
$0 validate "*/15 * * * *"
# Aplicar plantilla
$0 apply root backup_daily
# Generar reporte
$0 report /tmp/cron_report.html
ARCHIVOS:
Backups: $BACKUP_DIR
Plantillas: $TEMPLATES_DIR
Log: $LOG_FILE
EOF
}
# Función principal
main() {
case "${1:-help}" in
add)
if [ $# -lt 4 ]; then
echo "Error: Argumentos insuficientes para 'add'"
echo "Uso: $0 add USER SCHEDULE COMMAND [DESCRIPTION]"
exit 1
fi
add_cron_job "$2" "$3" "$4" "${5:-Tarea automatizada}"
;;
remove)
if [ $# -lt 3 ]; then
echo "Error: Argumentos insuficientes para 'remove'"
echo "Uso: $0 remove USER PATTERN"
exit 1
fi
remove_cron_job "$2" "$3"
;;
list)
list_cron_jobs "${2:-$(whoami)}"
;;
validate)
if [ $# -lt 2 ]; then
echo "Error: Se requiere expresión cron"
echo "Uso: $0 validate \"CRON_EXPRESSION\""
exit 1
fi
if validate_cron_expression "$2"; then
echo "✓ Expresión cron válida: $2"
else
exit 1
fi
;;
explain)
if [ $# -lt 2 ]; then
echo "Error: Se requiere expresión cron"
echo "Uso: $0 explain \"CRON_EXPRESSION\""
exit 1
fi
local explanation
explanation=$(explain_cron_schedule "$2")
echo "Expresión: $2"
echo "Explicación: ${explanation:-'No se pudo explicar esta expresión'}"
;;
backup)
backup_crontab "${2:-$(whoami)}"
;;
templates)
create_templates
;;
apply)
if [ $# -lt 3 ]; then
echo "Error: Argumentos insuficientes para 'apply'"
echo "Uso: $0 apply USER TEMPLATE_NAME"
exit 1
fi
apply_template "$2" "$3"
;;
monitor)
monitor_cron_execution "${2:-300}"
;;
report)
generate_cron_report "${2:-}"
;;
help|--help|-h)
show_help
;;
*)
echo "Comando no válido: $1"
show_help
exit 1
;;
esac
}
# Verificar si se ejecuta como script principal
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
main "$@"
fi
./cron_manager.sh add root "0 2 * * *" "/usr/local/bin/backup.sh" "Backup diario"
./cron_manager.sh list
./cron_manager.sh validate "*/15 * * * *"
#!/bin/bash
# Creador de systemd timers
# Alternativa moderna a cron
set -euo pipefail
# Configuración
SYSTEMD_USER_DIR="$HOME/.config/systemd/user"
SYSTEMD_SYSTEM_DIR="/etc/systemd/system"
# Función de logging
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}
# Crear servicio systemd
create_systemd_service() {
local service_name="$1"
local description="$2"
local command="$3"
local working_dir="${4:-/tmp}"
local user="${5:-$USER}"
local is_system="${6:-false}"
local service_file
if [ "$is_system" = "true" ]; then
service_file="$SYSTEMD_SYSTEM_DIR/$service_name.service"
mkdir -p "$SYSTEMD_SYSTEM_DIR"
else
service_file="$SYSTEMD_USER_DIR/$service_name.service"
mkdir -p "$SYSTEMD_USER_DIR"
fi
log "Creando servicio: $service_file"
cat > "$service_file" << EOF
[Unit]
Description=$description
After=network.target
[Service]
Type=oneshot
User=$user
WorkingDirectory=$working_dir
ExecStart=$command
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
log "✓ Servicio creado: $service_name.service"
}
# Crear timer systemd
create_systemd_timer() {
local timer_name="$1"
local description="$2"
local schedule="$3"
local is_system="${4:-false}"
local timer_file
if [ "$is_system" = "true" ]; then
timer_file="$SYSTEMD_SYSTEM_DIR/$timer_name.timer"
else
timer_file="$SYSTEMD_USER_DIR/$timer_name.timer"
fi
log "Creando timer: $timer_file"
cat > "$timer_file" << EOF
[Unit]
Description=$description Timer
Requires=$timer_name.service
[Timer]
$schedule
Persistent=true
[Install]
WantedBy=timers.target
EOF
log "✓ Timer creado: $timer_name.timer"
}
# Convertir expresión cron a systemd
cron_to_systemd() {
local cron_expr="$1"
local systemd_schedule=""
# Separar campos de cron
IFS=' ' read -ra FIELDS <<< "$cron_expr"
if [ ${#FIELDS[@]} -ne 5 ]; then
echo "Error: Expresión cron inválida"
return 1
fi
local minute="${FIELDS[0]}"
local hour="${FIELDS[1]}"
local day="${FIELDS[2]}"
local month="${FIELDS[3]}"
local weekday="${FIELDS[4]}"
# Casos especiales comunes
case "$cron_expr" in
"0 0 * * *")
systemd_schedule="OnCalendar=daily"
;;
"0 * * * *")
systemd_schedule="OnCalendar=hourly"
;;
"0 0 * * 1")
systemd_schedule="OnCalendar=weekly"
;;
"0 0 1 * *")
systemd_schedule="OnCalendar=monthly"
;;
"*/5 * * * *")
systemd_schedule="OnCalendar=*:0/5"
;;
"*/15 * * * *")
systemd_schedule="OnCalendar=*:0/15"
;;
"*/30 * * * *")
systemd_schedule="OnCalendar=*:0/30"
;;
*)
# Construcción más compleja
local time_spec=""
local date_spec=""
# Construir especificación de tiempo
if [ "$minute" = "*" ] && [ "$hour" = "*" ]; then
time_spec="*:*"
elif [ "$minute" = "0" ] && [ "$hour" = "*" ]; then
time_spec="*:00"
elif [ "$minute" != "*" ] && [ "$hour" != "*" ]; then
time_spec=$(printf "%02d:%02d" "$hour" "$minute")
fi
# Construir especificación de fecha
if [ "$weekday" != "*" ]; then
case "$weekday" in
0|7) date_spec="Sun" ;;
1) date_spec="Mon" ;;
2) date_spec="Tue" ;;
3) date_spec="Wed" ;;
4) date_spec="Thu" ;;
5) date_spec="Fri" ;;
6) date_spec="Sat" ;;
esac
fi
if [ -n "$date_spec" ]; then
systemd_schedule="OnCalendar=$date_spec $time_spec"
else
systemd_schedule="OnCalendar=$time_spec"
fi
;;
esac
echo "$systemd_schedule"
}
# Crear tarea programada completa
create_scheduled_task() {
local name="$1"
local description="$2"
local command="$3"
local cron_schedule="$4"
local working_dir="${5:-/tmp}"
local user="${6:-$USER}"
local is_system="${7:-false}"
log "Creando tarea programada: $name"
# Crear servicio
create_systemd_service "$name" "$description" "$command" "$working_dir" "$user" "$is_system"
# Convertir horario cron a systemd
local systemd_schedule
systemd_schedule=$(cron_to_systemd "$cron_schedule")
if [ $? -ne 0 ]; then
log "Error: No se pudo convertir el horario cron"
return 1
fi
# Crear timer
create_systemd_timer "$name" "$description" "$systemd_schedule" "$is_system"
# Recargar systemd y habilitar timer
if [ "$is_system" = "true" ]; then
systemctl daemon-reload
systemctl enable "$name.timer"
systemctl start "$name.timer"
log "✓ Timer del sistema habilitado e iniciado"
else
systemctl --user daemon-reload
systemctl --user enable "$name.timer"
systemctl --user start "$name.timer"
log "✓ Timer de usuario habilitado e iniciado"
fi
log "✓ Tarea programada '$name' creada exitosamente"
}
# Listar timers activos
list_timers() {
local scope="${1:-user}"
echo "=== TIMERS SYSTEMD ($scope) ==="
echo ""
if [ "$scope" = "system" ]; then
systemctl list-timers --no-pager
else
systemctl --user list-timers --no-pager 2>/dev/null || {
echo "No hay timers de usuario activos"
echo "Para habilitar timers de usuario persistentes:"
echo " sudo loginctl enable-linger $USER"
}
fi
}
# Mostrar estado de un timer específico
show_timer_status() {
local timer_name="$1"
local scope="${2:-user}"
echo "=== ESTADO DEL TIMER: $timer_name ==="
echo ""
if [ "$scope" = "system" ]; then
systemctl status "$timer_name.timer" --no-pager
echo ""
echo "=== HISTORIAL DE EJECUCIONES ==="
journalctl -u "$timer_name.service" --no-pager -n 10
else
systemctl --user status "$timer_name.timer" --no-pager
echo ""
echo "=== HISTORIAL DE EJECUCIONES ==="
journalctl --user -u "$timer_name.service" --no-pager -n 10
fi
}
# Eliminar tarea programada
remove_scheduled_task() {
local name="$1"
local scope="${2:-user}"
log "Eliminando tarea programada: $name"
if [ "$scope" = "system" ]; then
# Detener y deshabilitar timer
systemctl stop "$name.timer" 2>/dev/null || true
systemctl disable "$name.timer" 2>/dev/null || true
# Eliminar archivos
rm -f "$SYSTEMD_SYSTEM_DIR/$name.timer"
rm -f "$SYSTEMD_SYSTEM_DIR/$name.service"
systemctl daemon-reload
else
# Timer de usuario
systemctl --user stop "$name.timer" 2>/dev/null || true
systemctl --user disable "$name.timer" 2>/dev/null || true
# Eliminar archivos
rm -f "$SYSTEMD_USER_DIR/$name.timer"
rm -f "$SYSTEMD_USER_DIR/$name.service"
systemctl --user daemon-reload
fi
log "✓ Tarea '$name' eliminada"
}
# Crear ejemplos comunes
create_examples() {
log "Creando ejemplos de tareas systemd"
# Backup diario
create_scheduled_task \
"daily-backup" \
"Backup diario del sistema" \
"/usr/local/bin/backup_script.sh" \
"0 2 * * *" \
"/var/backups" \
"root" \
"true"
# Limpieza de logs
create_scheduled_task \
"cleanup-logs" \
"Limpieza de archivos de log antiguos" \
"/usr/bin/find /var/log -name '*.log' -mtime +7 -delete" \
"0 3 * * 0" \
"/var/log" \
"root" \
"true"
# Monitoreo de usuario
create_scheduled_task \
"user-monitor" \
"Monitoreo de recursos del usuario" \
"/home/$USER/bin/monitor.sh" \
"*/5 * * * *" \
"/home/$USER" \
"$USER" \
"false"
log "✓ Ejemplos creados"
}
# Función de ayuda
show_help() {
cat << EOF
Creador de Systemd Timers v1.0
USO:
$0 COMANDO [OPCIONES]
COMANDOS:
create NAME "DESCRIPTION" "COMMAND" "CRON_SCHEDULE" [WORKDIR] [USER] [SYSTEM]
Crear nueva tarea programada con systemd
list [system|user]
Listar timers activos
status TIMER_NAME [system|user]
Mostrar estado de un timer específico
remove TIMER_NAME [system|user]
Eliminar tarea programada
convert "CRON_EXPRESSION"
Convertir expresión cron a formato systemd
examples
Crear ejemplos de tareas comunes
help
Mostrar esta ayuda
EJEMPLOS:
# Crear backup diario del sistema
$0 create daily-backup "Backup diario" "/usr/local/bin/backup.sh" "0 2 * * *" "/var/backups" "root" "true"
# Crear monitoreo cada 5 minutos (usuario)
$0 create user-monitor "Monitor de recursos" "\$HOME/bin/monitor.sh" "*/5 * * * *"
# Listar timers del sistema
$0 list system
# Ver estado de un timer
$0 status daily-backup system
# Convertir expresión cron
$0 convert "*/15 * * * *"
VENTAJAS DE SYSTEMD TIMERS SOBRE CRON:
• Mejor logging con journald
• Dependencias entre servicios
• Manejo de errores más robusto
• Capacidad de recuperación tras fallos
• Integración con systemd
• Timers persistentes
ARCHIVOS:
Sistema: $SYSTEMD_SYSTEM_DIR
Usuario: $SYSTEMD_USER_DIR
EOF
}
# Función principal
main() {
case "${1:-help}" in
create)
if [ $# -lt 5 ]; then
echo "Error: Argumentos insuficientes para 'create'"
echo "Uso: $0 create NAME \"DESCRIPTION\" \"COMMAND\" \"CRON_SCHEDULE\" [WORKDIR] [USER] [SYSTEM]"
exit 1
fi
create_scheduled_task "$2" "$3" "$4" "$5" "${6:-/tmp}" "${7:-$USER}" "${8:-false}"
;;
list)
list_timers "${2:-user}"
;;
status)
if [ $# -lt 2 ]; then
echo "Error: Se requiere nombre del timer"
exit 1
fi
show_timer_status "$2" "${3:-user}"
;;
remove)
if [ $# -lt 2 ]; then
echo "Error: Se requiere nombre del timer"
exit 1
fi
remove_scheduled_task "$2" "${3:-user}"
;;
convert)
if [ $# -lt 2 ]; then
echo "Error: Se requiere expresión cron"
exit 1
fi
local result
result=$(cron_to_systemd "$2")
echo "Cron: $2"
echo "Systemd: $result"
;;
examples)
create_examples
;;
help|--help|-h)
show_help
;;
*)
echo "Comando no válido: $1"
show_help
exit 1
;;
esac
}
# Ejecutar función principal
main "$@"
Crea un sistema completo de backups programados:
Desarrolla tareas de mantenimiento automático: