Cerrábamos el Módulo 2 con un límite muy concreto: un alias no acepta argumentos, una tubería de cinco filtros no se puede documentar ni versionar, y nada de lo que tecleas hoy se ejecutará mañana a las siete de la mañana sin ti delante. Esta lección rompe ese límite. Vas a guardar esas líneas en un fichero, decirle al sistema cómo ejecutarlo y convertirlo en un comando de pleno derecho dentro de ~/veloz-ops/bin. Al terminar tendrás la primera versión real de informe-diario.sh, el script que crecerá contigo durante todo el módulo hasta convertirse en la herramienta central del toolkit de Veloz Envíos.

Contenido

  1. Qué es un script y cuándo merece la pena escribirlo
  2. El shebang: la primera línea que lo cambia todo
  3. Las cuatro formas de ejecutar un script
  4. Permisos de ejecución y por qué hace falta el ./
  5. Anatomía de un script bien formado
  6. Comentarios que sirven para algo
  7. Códigos de salida: exit N como contrato
  8. Nombres y ubicación dentro del toolkit
  9. informe-diario.sh, versión 1

  1. Qué es un script y cuándo merece la pena escribirlo

Un script de Bash es un fichero de texto plano con comandos, uno por línea, que el shell lee y ejecuta de arriba abajo. No hay compilación, no hay proyecto, no hay dependencias: exactamente los mismos comandos que tecleas en la terminal, guardados en un fichero.

Esa simplicidad es engañosa, porque lo que aporta el fichero no es potencia sino permanencia. Compáralo con lo que has hecho hasta ahora:

Necesidad Tubería tecleada Alias Script
Ejecutar mañana sin recordarla No
Aceptar argumentos Sí (reescribiendo) No
Varias líneas y lógica Muy incómodo No
Documentarse y versionarse No Apenas
Ejecutarse sola de madrugada No No
Compartirse con un compañero Copiando texto No

La regla práctica para decidir es sencilla y merece que la interiorices: si vas a repetir una secuencia más de dos o tres veces, o si necesitas que alguien más la ejecute exactamente igual, escribe un script. Para una consulta puntual, la terminal sigue siendo la herramienta correcta; escribir un script para algo que harás una sola vez es trabajo perdido.

Hay un tercer criterio, menos obvio pero decisivo en operaciones: si el error humano es caro, escribe un script. Una cadena de filtros que teclees cada mañana acabará teniendo una errata algún día. El script la teclea siempre igual.

  1. El shebang: la primera línea que lo cambia todo

Cuando el núcleo de Linux ejecuta un fichero, necesita saber qué intérprete debe leerlo. Esa información va en la primerísima línea, que empieza por los caracteres #! (llamados shebang, de sharp + bang) seguidos de la ruta del intérprete:

#!/usr/bin/env bash

El shebang debe ser la línea 1, columna 1. Ni una línea en blanco antes, ni un espacio delante del #. Para el shell es un comentario más (empieza por #), pero para el núcleo es una instrucción.

Existen dos formas habituales y conviene entender la diferencia:

Forma Cómo funciona Ventajas Inconvenientes
#!/bin/bash Ejecuta el binario de esa ruta exacta Explícito, sin intermediarios; inmune a un PATH manipulado Falla si Bash no está en /bin (macOS con Homebrew, algunos BSD); usa siempre el Bash del sistema, aunque sea antiguo
#!/usr/bin/env bash Ejecuta env, que busca bash en el PATH Portable entre sistemas; respeta versiones instaladas por el usuario Depende del PATH; no admite opciones adicionales de forma portable

Recomendación para este curso: #!/usr/bin/env bash. Es el estándar de facto en scripts que se comparten, y en srv-veloz-01 funciona igual de bien. Lo que nunca debes escribir si usas funciones propias de Bash es #!/bin/sh: en Ubuntu 24.04 ese enlace apunta a dash, un shell POSIX mucho más limitado que no entiende [[ ]], arrays ni muchas otras cosas que usarás a partir de la lección siguiente (la portabilidad POSIX se trata a fondo en 08-07).

¿Y si falta el shebang? El script no da error necesariamente, y eso es lo peligroso. Ocurre lo siguiente:

  • Si lo ejecutas con bash informe-diario.sh, funciona: tú ya has dicho qué intérprete usar.
  • Si lo ejecutas con ./informe-diario.sh, el núcleo no sabe qué hacer con él y el shell interactivo lo intenta ejecutar con una copia de sí mismo. En tu terminal Bash parecerá que funciona.
  • Si lo ejecuta cron (Módulo 7), un servicio de systemd u otro usuario con otro shell, se interpretará con sh y fallará de formas desconcertantes.

Es decir: la ausencia de shebang produce un script que funciona en tu terminal y falla en producción, que es el peor tipo de fallo posible. Por eso el shebang no es un adorno.

  1. Las cuatro formas de ejecutar un script

Hay cuatro maneras de lanzar un script y no son equivalentes. Entender la diferencia conecta directamente con los subshells de la lección 01-04.

bash ~/veloz-ops/bin/informe-diario.sh    # 1. intérprete explícito
./informe-diario.sh                       # 2. ejecutable, indicando su ruta
informe-diario.sh                         # 3. por su nombre, vía PATH
source ~/veloz-ops/bin/informe-diario.sh  # 4. source (abreviado: un punto)
. ~/veloz-ops/bin/informe-diario.sh       #    equivalente al anterior
Forma ¿Necesita permiso x? ¿Respeta el shebang? ¿Dónde se ejecuta?
bash script.sh No No (lo ignora, ya has elegido tú) Proceso Bash nuevo (subshell)
./script.sh Proceso nuevo según el shebang
script.sh (por PATH) Proceso nuevo según el shebang
source script.sh No No En tu shell actual

Las tres primeras crean un proceso hijo. Aquí revive la lección 01-04: las variables que el script defina mueren con él, y un cd dentro del script no cambia tu directorio actual. Eso es exactamente lo que quieres de una herramienta: que haga su trabajo y no toque tu sesión.

source es la excepción radical: no crea proceso nuevo, ejecuta las líneas del fichero dentro de tu shell. Por eso source ~/.bashrc aplica los alias a la sesión en curso (lección 01-02), y por eso source es la forma correcta de cargar un fichero de configuración o una librería de funciones —el patrón que usarás con ~/veloz-ops/etc/veloz-ops.conf y que se formaliza en 05-06—. Y por eso mismo no debes usar source para ejecutar una herramienta: si el script hace exit 1, con source estarás cerrando tu propia terminal.

  1. Permisos de ejecución y por qué hace falta el ./

Un fichero recién creado no es ejecutable. Recupera el bit x de la lección 02-03:

ls -l ~/veloz-ops/bin/informe-diario.sh
-rw-rw-r-- 1 joan joan 412 ago  3 12:04 /home/joan/veloz-ops/bin/informe-diario.sh

Sin x en los permisos, ./informe-diario.sh responde Permiso denegado. Se arregla con:

chmod +x ~/veloz-ops/bin/informe-diario.sh   # rápida, respeta el umask
chmod 755 ~/veloz-ops/bin/informe-diario.sh  # explícita: rwxr-xr-x

755 es el permiso canónico de un script en bin/: el dueño puede leer, escribir y ejecutar; los demás pueden leer y ejecutar. Si el script contuviera secretos usarías 700, pero la buena práctica es que los secretos vivan en etc/veloz-ops.conf con permisos 600, no en el código.

Queda la pregunta del ./. Cuando escribes un nombre suelto, Bash lo busca solo en los directorios del PATH, y el directorio actual no está en el PATH (ni debe estarlo: si lo estuviera, un fichero malicioso llamado ls en un directorio compartido se ejecutaría en lugar del ls real). El ./ convierte el nombre en una ruta relativa explícita —«el fichero llamado así, en este directorio»— y desactiva la búsqueda por PATH. Es una medida de seguridad, no un capricho de sintaxis.

  1. Anatomía de un script bien formado

Un script profesional tiene una estructura reconocible. Este es el esqueleto que usarás a partir de ahora:

#!/usr/bin/env bash
#
# informe-diario.sh - Resumen diario de actividad de Veloz Envíos
#
# Propósito : Contar los errores de app.log y agrupar los envíos por estado.
# Autor     : Joan Costa <[email protected]>
# Creado    : 2026-08-03
# Uso       : informe-diario.sh
# Salida    : Informe por stdout.
# Códigos   : 0 correcto | 1 error de ejecución
#

# --- Cuerpo -----------------------------------------------------------
echo "hola desde el toolkit"

exit 0    # --- Salida ---

Las cuatro partes, por orden: shebang (línea 1, sin excepciones); cabecera de comentarios con qué hace, quién lo escribió, cómo se usa y qué devuelve —dentro de seis meses, esa cabecera serás tú explicándote a ti mismo por qué existe este fichero—; cuerpo, con los comandos agrupados en bloques separados visualmente; y exit final con el código explícito. Esa estructura es la convención que espera cualquiera que abra el fichero, y en 08-01 la ampliaremos con criterios de legibilidad más finos.

  1. Comentarios que sirven para algo

Todo lo que sigue a # hasta el final de la línea se ignora (salvo el shebang de la línea 1), tanto en línea propia como al final de un comando. Bash no tiene comentarios de bloque; para comentar varias líneas se pone # en cada una, cosa que cualquier editor hace con un atajo.

El error clásico del principiante es comentar lo que hace el comando, que ya se lee en el propio comando. Lo valioso es comentar por qué:

# MAL: cuenta los errores
grep -c ERROR /var/log/veloz/app.log

# BIEN: veloz-api registra los 500 como ERROR; este recuento alimenta el aviso
#       diario a soporte. Umbral acordado con negocio: 50 errores/día.
grep -c ERROR /var/log/veloz/app.log

  1. Códigos de salida: exit N como contrato

En la lección 01-04 conociste $?. Ahora te toca producirlo. Cuando tu script termina, devuelve un número entre 0 y 255 que es su única forma de comunicarse con quien lo llamó: otro script, cron, systemd o un canal de CI.

  • exit 0 significa «todo correcto». Es el único valor que significa éxito.
  • exit N con N distinto de 0 significa error, y el número concreto indica qué error.

Si omites exit, el script devuelve el código del último comando ejecutado, que casi nunca es lo que quieres comunicar. Sé explícito. Algunos códigos tienen significado convenido en el sistema:

Código Significado
0 Éxito
1 Error genérico
2 Uso incorrecto del comando (faltan argumentos, opción desconocida)
3 Errores específicos que defines tú: p. ej. falta el fichero de datos
126 El fichero existe pero no es ejecutable (falta el x)
127 Comando no encontrado (no está en el PATH, o el shebang apunta a un intérprete inexistente)
130 Terminado con Ctrl-C (128 + señal 2)

Los códigos 126 y 127 son tus dos mejores pistas de diagnóstico en esta lección: 126 es «te falta chmod +x» y 127 es «el nombre o el shebang están mal».

Este contrato es lo que permite escribir informe-diario.sh && enviar-correo.sh, con el && de la lección 03-03: el correo solo se envía si el informe terminó bien.

  1. Nombres y ubicación dentro del toolkit

Convenciones que aplicaremos en ~/veloz-ops/bin durante todo el curso:

  • Minúsculas y guiones, nombre descriptivo: informe-diario.sh, rotar-logs.sh. Nada de espacios, tildes, mayúsculas ni script2.sh. El nombre debe decir qué hace.
  • Extensión .sh: útil mientras aprendes, porque identifica el lenguaje y activa el resaltado del editor. En herramientas maduras suele omitirse (los comandos del sistema no la llevan); mantendremos .sh por claridad didáctica.
  • Ubicación en bin/: como ~/veloz-ops/bin ya está en tu PATH desde 01-02, cualquier script que dejes ahí y marques como ejecutable se convierte automáticamente en un comando del sistema, invocable desde cualquier directorio y sin ./.

  1. informe-diario.sh, versión 1

Ha llegado el momento. Vamos a trasladar al fichero las tuberías que ya escribías a mano en el Módulo 2.

Abre el fichero con nano ~/veloz-ops/bin/informe-diario.sh y escribe:

#!/usr/bin/env bash
#
# informe-diario.sh - Resumen diario de actividad de Veloz Envíos
#
# Propósito : Contar los ERROR de app.log y agrupar los envíos por estado.
# Autor     : Joan Costa <[email protected]>
# Creado    : 2026-08-03
# Uso       : informe-diario.sh
# Códigos   : 0 correcto
#

echo "==================================================="
echo "  INFORME DIARIO - VELOZ ENVIOS"
date +"  Generado: %F %T"
echo "==================================================="

# --- Errores registrados por la aplicación ---------------------------
echo
echo "-- Errores en app.log --"
grep -c ERROR /var/log/veloz/app.log

# --- Envíos por estado -----------------------------------------------
echo
echo "-- Envíos por estado --"
tail -n +2 /srv/veloz/datos/envios.csv | cut -d, -f5 | sort | uniq -c | sort -rn

exit 0

Guárdalo, dale permisos y ejecútalo:

chmod 755 ~/veloz-ops/bin/informe-diario.sh
informe-diario.sh
===================================================
  INFORME DIARIO - VELOZ ENVIOS
  Generado: 2026-08-03 12:11:47
===================================================

-- Errores en app.log --
37

-- Envíos por estado --
    981 entregado
    148 en_reparto
     118 incidencia

Repasa lo que acaba de ocurrir. No lo has invocado con ./ ni con la ruta completa: has escrito informe-diario.sh a secas, desde cualquier directorio, porque ~/veloz-ops/bin está en el PATH y el fichero tiene el bit x. Acabas de crear un comando nuevo en srv-veloz-01.

Fíjate también en tail -n +2, que descarta la línea de cabecera del CSV: sin él, la palabra estado aparecería como si fuera un estado más. Y en que el script escribe en stdout, sin redirigir nada, lo cual es deliberado: quien lo llame decidirá si quiere verlo en pantalla, guardarlo con > informe.txt o ambas cosas con tee. Un script bien hecho no decide por su invocador.

Este script tiene todavía una debilidad evidente: las rutas y los números están escritos a pelo, repartidos por el fichero. Si envios.csv cambia de sitio, hay que buscar y reemplazar. Ese es justo el problema que resuelve la lección siguiente.

Errores Comunes y Consejos

  • Olvidar chmod +x. Síntoma: Permiso denegado y código de salida 126. Es, con diferencia, el primer tropiezo de todo el mundo.
  • Finales de línea CRLF. Si editas el script en Windows, cada línea acaba con \r invisible y verás mensajes absurdos como bash: ./informe-diario.sh: /usr/bin/env bash^M: no such file or directory o bad interpreter. Diagnostícalo con file informe-diario.sh (dirá with CRLF line terminators) y arréglalo con dos2unix informe-diario.sh o sed -i 's/\r$//' informe-diario.sh.
  • Espacio o línea en blanco antes del shebang. Deja de ser shebang y pasa a ser un comentario cualquiera.
  • Escribir #!/bin/sh usando sintaxis de Bash. En Ubuntu eso es dash: fallará en [[ ]], arrays y aritmética. Si es Bash, dilo.
  • Ejecutar herramientas con source. Un exit dentro cerrará tu terminal y las variables del script contaminarán tu sesión. source es para configuración y librerías.
  • Editar un script del sistema sin permisos. Si nano te deja escribir pero no guardar, has perdido el trabajo. Comprueba antes con ls -l o edita con sudoedit.
  • Nombrar un script igual que un comando existente. Un fichero llamado test o ls en tu bin/ provocará caos. Verifica antes con type -a nombre (lección 01-04).

Ejercicios

Ejercicio 1 — Diagnóstico de un script que no arranca. Un compañero ha creado /home/juan/veloz-ops/bin/resumen.sh, pero al ejecutar resumen.sh obtiene bash: resumen.sh: orden no encontrada, y con ./resumen.sh obtiene bash: ./resumen.sh: Permiso denegado. Explica ambos mensajes y da los comandos que lo arreglan.

Ejercicio 2 — Tu segundo script. Crea ~/veloz-ops/bin/estado-servidor.sh con cabecera completa, que muestre el nombre del servidor, el tiempo encendido, el espacio libre en disco y las tres IPs que más han accedido según acceso.log. Debe terminar con exit 0 y ser invocable por su nombre desde cualquier directorio.

Ejercicio 3 — Elegir la forma de ejecución. Para cada caso, indica cuál de las cuatro formas es la correcta y por qué: (a) ejecutar el informe diario desde cron a las 07:00; (b) cargar las variables de ~/veloz-ops/etc/veloz-ops.conf en tu sesión; (c) probar un script recién descargado sin darle permisos de ejecución; (d) un script que debe cambiar el directorio actual de tu terminal.

Soluciones

Solución al Ejercicio 1

chmod 755 ~/veloz-ops/bin/resumen.sh     # da el bit x que faltaba
echo $PATH | tr ':' '\n' | grep veloz    # comprueba que bin/ está en el PATH
hash -r                                  # refresca la caché de rutas de Bash

Son dos fallos distintos, y por eso hay dos mensajes distintos. orden no encontrada (código 127) significa que Bash recorrió el PATH y no encontró ningún fichero llamado resumen.sh: o el directorio no está en el PATH, o Bash tiene cacheada una versión antigua (de ahí hash -r). Permiso denegado (código 126) es el otro problema: con ./ sí encuentra el fichero, pero le falta el bit x. Conviene resolver ambos, porque arreglar solo los permisos dejaría el script sin poder invocarse por su nombre.

Solución al Ejercicio 2

#!/usr/bin/env bash
#
# estado-servidor.sh - Estado resumido de srv-veloz-01
#
# Propósito : Vista rápida de salud del servidor y del tráfico a veloz-api.
# Autor     : Joan Costa <[email protected]>
# Uso       : estado-servidor.sh
# Códigos   : 0 correcto
#

echo "-- Servidor --"; hostname; uptime

echo; echo "-- Disco --"
df -h /srv /var/log

echo; echo "-- Top 3 IPs en acceso.log --"
cut -d' ' -f1 /var/log/veloz/acceso.log | sort | uniq -c | sort -rn | head -3

exit 0

Recuerda rematarlo con chmod 755 ~/veloz-ops/bin/estado-servidor.sh antes de invocarlo por su nombre. Observa que df -h /srv /var/log limita la salida a las particiones que importan en lugar de listar todos los sistemas de ficheros: un informe operativo debe responder una pregunta, no volcar datos.

Solución al Ejercicio 3

Caso Forma correcta Motivo
(a) Desde cron a las 07:00 Ruta absoluta ejecutable: /home/joan/veloz-ops/bin/informe-diario.sh cron arranca con un PATH mínimo y sin tu ~/.bashrc; nunca supongas que tu PATH existe ahí (Módulo 7)
(b) Cargar veloz-ops.conf source ~/veloz-ops/etc/veloz-ops.conf Las variables deben quedar en tu shell; un subshell moriría con ellas
(c) Probar sin dar permisos bash script.sh No requiere el bit x y, además, te permite añadir bash -x para ver cada línea antes de confiar en él
(d) Cambiar el directorio actual source (o mejor, una función) Un proceso hijo no puede cambiar el directorio de su padre; es una limitación del sistema, no de Bash (lección 01-04)

El caso (d) merece un matiz: que un script deba modificar tu sesión suele ser señal de que lo que quieres no es un script sino una función de shell, y esas llegan en 04-02.

Conclusión

Has dado el salto del comando al programa. Sabes qué es un script y cuándo no merece la pena escribirlo; escribes el shebang correcto y entiendes por qué su ausencia produce fallos que solo aparecen en producción; distingues las cuatro formas de ejecución y sabes que source es una categoría aparte porque no crea subshell; das permisos con chmod 755 y comprendes que el ./ es una defensa de seguridad; estructuras el fichero con cabecera, cuerpo y exit; comentas el porqué en lugar del qué; y devuelves códigos de salida que convierten tu script en una pieza componible con &&.

Sobre todo, tienes informe-diario.sh funcionando en ~/veloz-ops/bin e invocable por su nombre desde cualquier punto de srv-veloz-01. Es un comando de verdad, aunque todavía rígido: /var/log/veloz/app.log y /srv/veloz/datos/envios.csv están escritos literalmente en mitad del fichero, y el umbral de errores que quieres vigilar no está en ninguna parte.

En la lección 03-02 le damos memoria. Aprenderás a guardar valores en variables, a fijar las rutas y los umbrales como constantes al principio del fichero con readonly, y a capturar la salida de un comando dentro de una variable con $(...). A partir de ahí, cambiar una ruta será modificar una línea, y informe-diario.sh empezará a parecerse a un programa.

Curso de Programación en Bash

Módulo 1: Introducción a Bash

Módulo 2: Comandos Básicos de Bash

Módulo 3: Fundamentos de Scripting

Módulo 4: Scripting Intermedio

Módulo 5: Técnicas Avanzadas de Scripting

Módulo 6: Trabajando con Herramientas Externas

Módulo 7: Automatización y Programación

Módulo 8: Mejores Prácticas y Optimización

Módulo 9: Proyectos del Mundo Real

© Copyright 2026. Todos los derechos reservados