Autor: ciberkaos

  • 5. Bucles y Estructuras de Repetición

    Los bucles permiten ejecutar un conjunto de instrucciones varias veces. Son esenciales en automatización, análisis de archivos, procesado de datos, monitoreo y en general cualquier script útil.

    En Bash disponemos de varias estructuras de repetición: for, while, until y control de bucles mediante break y continue.


    1. Bucle for

    Ideal para iterar sobre listas, rangos o elementos concretos.

    1.1. Iterar sobre una lista

    Sintaxis:

    
    
    
    
    
    for var in elemento1 elemento2 elemento3; do
        comandos
    done
    

    Ejemplo:

    
    
    
    
    
    for animal in perro gato loro; do
        echo "Animal: $animal"
    done
    

    Salida:

    
    
    
    
    
    Animal: perro
    Animal: gato
    Animal: loro
    

    1.2. Iterar sobre un rango

    
    
    
    
    
    for i in {1..5}; do
        echo "Número: $i"
    done
    

    Salida:

    
    
    
    
    
    Número: 1
    Número: 2
    Número: 3
    Número: 4
    Número: 5
    

    1.3. Iterar sobre archivos

    
    
    
    
    
    for archivo in *.txt; do
        echo "Procesando $archivo"
    done
    

    Muy útil para automatizar tareas sobre lotes de archivos.


    2. Bucle while

    Ejecuta instrucciones mientras se cumpla una condición.

    Sintaxis:

    
    
    
    
    
    while [ condición ]; do
        comandos
    done
    

    Ejemplo:

    
    
    
    
    
    contador=1
    while [ $contador -le 5 ]; do
        echo "Contador: $contador"
        contador=$((contador + 1))
    done
    

    2.1. Leer archivo línea a línea

    Excelente para procesar logs o datos estructurados.

    
    
    
    
    
    while read linea; do
        echo "$linea"
    done < archivo.txt
    

    3. Bucle until

    Es lo inverso a while: ejecuta hasta que se cumpla una condición.

    Sintaxis:

    
    
    
    
    
    until [ condición ]; do
        comandos
    done
    

    Ejemplo:

    
    
    
    
    
    contador=1
    until [ $contador -gt 5 ]; do
        echo "Contador: $contador"
        contador=$((contador + 1))
    done
    

    Hace lo mismo que el ejemplo de while, pero con lógica inversa.


    4. Control del Flujo: break y continue

    break

    Termina el bucle antes de tiempo.

    Ejemplo:

    
    
    
    
    
    for i in {1..10}; do
        if [ $i -eq 5 ]; then
            break
        fi
        echo "i = $i"
    done
    

    Salida:

    
    
    
    
    
    i = 1
    i = 2
    i = 3
    i = 4
    

    continue

    Salta a la siguiente iteración sin terminar el bucle.

    Ejemplo:

    
    
    
    
    
    for i in {1..5}; do
        if [ $i -eq 3 ]; then
            continue
        fi
        echo "i = $i"
    done
    

    Salida:

    
    
    
    
    
    i = 1
    i = 2
    i = 4
    i = 5
    

    5. Bucles Anidados

    Se puede meter un bucle dentro de otro.

    Ejemplo:

    
    
    
    
    
    for i in {1..3}; do
        for j in {1..2}; do
            echo "i=$i, j=$j"
        done
    done
    

    6. Ejemplos Aplicados

    Ejemplo 1: Contador simple

    
    
    
    
    
    #!/bin/bash
    
    for i in {1..10}; do
        echo "Número: $i"
    done
    

    Ejemplo 2: Procesar todos los .log

    
    
    
    
    
    #!/bin/bash
    
    for log in *.log; do
        echo "Analizando $log..."
    done
    

    Ejemplo 3: Ping a una subred

    
    
    
    
    
    #!/bin/bash
    
    for i in {1..254}; do
        ping -c 1 192.168.1.$i >/dev/null 2>&1
        if [ $? -eq 0 ]; then
            echo "192.168.1.$i está activo"
        fi
    done
    

    Ejemplo 4: Leer archivo línea a línea

    
    
    
    
    
    #!/bin/bash
    
    while read linea; do
        echo "Línea: $linea"
    done < usuarios.txt
    

    Ejercicios propuestos

    Ejercicio 1:
    Crear un script cuenta.sh que muestre los números del 1 al 20 usando for.

    Ejercicio 2:
    Crear un script files.sh que liste todos los archivos del directorio actual y muestre cuántos hay al final.

    Ejercicio 3:
    Crear un script ping.sh que pida una IP base (por ejemplo 192.168.1) y haga ping del .1 al .10, mostrando solo los que respondan.

    Ejercicio 4:
    Crear un script leer.sh que lea un archivo de texto pasado como parámetro y muestre cada línea precedida del número de línea:

    Ejemplo:

    
    
    
    
    
    1: línea uno
    2: línea dos
    3: línea tres
    

    Soluciones

    ✅ Ejercicio 1 — cuenta.sh

    Enunciado:
    Crear un script cuenta.sh que muestre los números del 1 al 20 usando for.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: cuenta.sh
    # Muestra los números del 1 al 20
    
    for i in {1..20}; do
        echo "$i"
    done
    

    Salida esperada:

    
    
    
    
    
    1
    2
    3
    ...
    20
    

    ✅ Ejercicio 2 — files.sh

    Enunciado:
    Crear un script files.sh que liste todos los archivos del directorio actual y muestre cuántos hay al final.

    💡 Solución sencilla (con ls y wc -l):

    
    
    
    
    
    #!/bin/bash
    # Script: files.sh
    # Lista los archivos del directorio actual y muestra cuántos hay
    
    echo "Archivos en el directorio actual:"
    ls
    
    echo
    total=$(ls | wc -1)
    echo "Total de entradas: $total"
    

    Nota: ls lista tanto archivos como directorios. Para un curso inicial está bien.

    💡 Versión algo más “didáctica” con for y contador:

    
    
    
    
    
    #!/bin/bash
    # Script: files.sh
    # Recorre las entradas del directorio actual y las cuenta
    
    contador=0
    
    echo "Entradas en el directorio actual:"
    for item in *; do
        echo "$item"
        contador=$((contador + 1))
    done
    
    echo
    echo "Total de entradas: $contador"
    

    ✅ Ejercicio 3 — ping.sh

    Enunciado:
    Crear un script ping.sh que pida una IP base (por ejemplo 192.168.1) y haga ping del .1 al .10, mostrando solo las IP que respondan.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: ping.sh
    # Hace ping a una serie de IPs de la misma red (del .1 al .10)
    
    read -p "Introduce la IP base (ej: 192.168.1): " base
    
    for i in {1..10}; do
        ip="$base.$i"
        ping -c 1 -W 1 "$ip" >/dev/null 2>&1
        if [ $? -eq 0 ]; then
            echo "$ip está ACTIVA"
        else
            echo "$ip no responde"
        fi
    done
    

    Si quieres que solo muestre las activas, quitas el else:

    
    
    
    
    
    #!/bin/bash
    # Solo muestra las IP que responden
    
    read -p "Introduce la IP base (ej: 192.168.1): " base
    
    for i in {1..10}; do
        ip="$base.$i"
        ping -c 1 -W 1 "$ip" >/dev/null 2>&1
        if [ $? -eq 0 ]; then
            echo "$ip está ACTIVA"
        fi
    done
    

    ✅ Ejercicio 4 — leer.sh

    Enunciado:
    Crear un script leer.sh que lea un archivo de texto pasado como parámetro y muestre cada línea precedida del número de línea:

    Ejemplo:

    
    
    
    
    
    1: línea uno
    2: línea dos
    3: línea tres
    

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: leer.sh
    # Muestra un archivo línea a línea con número de línea
    
    if [ $# -ne 1 ]; then
        echo "Uso: $0 archivo"
        exit 1
    fi
    
    archivo="$1"
    
    if [ ! -f "$archivo" ]; then
        echo "El archivo '$archivo' no existe o no es un archivo regular."
        exit 1
    fi
    
    num_linea=1
    while read linea; do
        echo "$num_linea: $linea"
        num_linea=$((num_linea + 1))
    done < "$archivo"
    

    Ejemplo de uso:

    
    
    
    
    
    ./leer.sh notas.txt
    

    Salida:

    
    
    
    
    
    1: primera línea del archivo
    2: segunda línea del archivo
    3: tercera línea...
    

  • 6. Arrays y Gestión de Colecciones

    6. Arrays y Gestión de Colecciones

    En Bash, un array es una variable que puede almacenar múltiples valores. Son muy útiles cuando queremos guardar listas de elementos (archivos, usuarios, números, etc.) y procesarlos más tarde con bucles.

    Este módulo te permitirá trabajar con colecciones simples y asociativas, acceder a valores por índice, añadir elementos y recorrer los arrays.


    1. Arrays Indexados

    Los arrays indexados usan números enteros como índices, empezando por 0.

    1.1 Crear un array

    Varias formas:

    
    
    
    
    
    nombres=("Ana" "Luis" "Carlos")
    

    o

    
    
    
    
    
    nombres[0]="Ana"
    nombres[1]="Luis"
    nombres[2]="Carlos"
    

    1.2 Acceder a elementos

    Acceso mediante índices:

    
    
    
    
    
    echo ${nombres[0]}
    

    Salida:

    
    
    
    
    
    Ana
    

    1.3 Mostrar todos los elementos

    
    
    
    
    
    echo ${nombres[@]}
    

    o

    
    
    
    
    
    echo ${nombres[*]}
    

    1.4 Contar elementos

    
    
    
    
    
    echo ${#nombres[@]}
    

    Ejemplo:

    
    
    
    
    
    nombres=("Ana" "Luis" "Carlos")
    echo ${#nombres[@]}    # muestra 3
    

    Cuando trabajas con arrays:

    SímboloEjemploSignificado
    @${array[@]}Todos los elementos del array por separado.
    *${array[*]}Todos los elementos como una única cadena.
    #${#array[@]}Número de elementos del array.
    []${array[2]}Acceder al índice 2 del array.


    ${array[@]} y ${array[*]} parecen iguales, pero no lo son cuando hay espacios:

    • "${array[@]}" → cada elemento es una palabra independiente
    • "${array[*]}" → todo se convierte en una única cadena

    Esto afecta a bucles, argumentos, etc.

    1.5 Añadir elementos

    
    
    
    
    
    nombres+=("Sofía")
    

    o por índice:

    
    
    
    
    
    nombres[3]="Sofía"
    

    1.6 Eliminar elementos

    Se puede borrar un índice concreto:

    
    
    
    
    
    unset nombres[1]
    

    Esto no reorganiza los índices, simplemente deja un “hueco”.


    2. Recorrer Arrays

    La manera más común es con for:

    
    
    
    
    
    nombres=("Ana" "Luis" "Carlos")
    
    for nombre in "${nombres[@]}"; do
        echo "Nombre: $nombre"
    done
    

    Salida:

    
    
    
    
    
    Nombre: Ana
    Nombre: Luis
    Nombre: Carlos
    

    3. Arrays Asociativos

    Los arrays asociativos permiten guardar pares clave → valor, similar a diccionarios o mapas.

    3.1 Declaración

    Hay que declararlos antes:

    
    
    
    
    
    declare -A capitales
    

    3.2 Asignación

    
    
    
    
    
    capitales[España]="Madrid"
    capitales[Francia]="París"
    capitales[Italia]="Roma"
    

    3.3 Acceso por clave

    
    
    
    
    
    echo ${capitales[Francia]}
    

    Salida:

    
    
    
    
    
    París
    

    3.4 Listar claves

    
    
    
    
    
    echo ${!capitales[@]}
    

    3.5 Listar valores

    
    
    
    
    
    echo ${capitales[@]}
    

    4. Casos de uso comunes

    4.1 Guardar usuarios del sistema

    
    
    
    
    
    usuarios=($(cut -d: -f1 /etc/passwd))
    

    4.2 Guardar archivos por extensión

    
    
    
    
    
    imagenes=(*.jpg *.png *.gif)
    

    4.3 Guardar resultados de un comando

    
    
    
    
    
    procesos=($(ps -e | awk '{print $1}'))
    

    5. Simbologías en bash

    En Bash aparecen unos símbolos “místicos” que parecen sacados de la iconografía nórdica (@, *, #, etc.) y que tienen significados distintos según el contexto.


    a) En arrays Bash

    Cuando trabajas con arrays:

    SímboloEjemploSignificado
    @${array[@]}Todos los elementos del array por separado.
    *${array[*]}Todos los elementos como una única cadena.
    #${#array[@]}Número de elementos del array.
    []${array[2]}Acceder al índice 2 del array.

    Matiz interesante:
    ${array[@]} y ${array[*]} parecen iguales, pero no lo son cuando hay espacios:

    • "${array[@]}" → cada elemento es una palabra independiente
    • "${array[*]}" → todo se convierte en una única cadena

    Esto afecta a bucles, argumentos, etc.


    b) En parámetros posicionales ($1, $2, etc.)

    Cuando el script recibe argumentos:

    SímboloEjemploSignificado
    @"$@"Todos los parámetros, cada uno como una palabra independiente.
    *"$*"Todos los parámetros como una sola palabra.
    #$#Número total de parámetros recibidos.
    0$0Nombre del script.
    1,2,3…$1, $2Parám. posicionales: primero, segundo, etc.

    Ejemplo visual:

    Si ejecutas:

    
    
    
    
    
    ./script.sh uno "dos tres" cuatro
    
    • "$@"uno dos tres cuatro (3 palabras)
    • "$*"uno dos tres cuatro (1 palabra grande)

    c) En globbing (patrones de archivos)

    Cuando expandes patrones en el shell:

    PatrónEjemploSignificado
    **.txtCualquier cadena (0 o más caracteres).
    ?t?.txtUn solo carácter desconocido.
    []t[aeiou].txtClase de caracteres posibles.
    [^ ]t[^a].txtClase negada (cualquiera menos “a”).
    {}{jpg,png}Expansión de listas.

    Ejemplos rápidos:

    • *.txt coincide con hola.txt, notas.txt, etc.
    • ???.txt coincide con uno.txt pero no con hola.txt.
    • f[oa]r.txt coincide con for.txt y far.txt.

    d) En cadenas (longitud y sustitución)

    El símbolo # también sirve para medir longitudes:

    SímboloEjemploSignificado
    #${#var}Longitud de la variable “var”.
    %${var%pat}Elimina el patrón más corto por la derecha.
    %%${var%%pat}Elimina el patrón más largo por la derecha.
    #${var#pat}Elimina el patrón más corto por la izquierda.
    ##${var##pat}Elimina el patrón más largo por la izquierda.

    Ejemplo rápido:

    
    
    
    
    
    var="archivo.tar.gz"
    echo ${var%.*}   # archivo.tar
    echo ${var%%.*}  # archivo
    

    Resumen

    Contexto@*#
    ArraysElementos separadosElementos juntosNúmero de elementos
    Parámetros del scriptArgs separadosArgs juntosNúmero de args
    GlobbingCualquier cadena
    CadenasLongitud

    6. Ejemplos Aplicados

    Ejemplo 1: Lista de frutas

    
    
    
    
    
    #!/bin/bash
    
    frutas=("Manzana" "Pera" "Uva")
    for fruta in "${frutas[@]}"; do
        echo "Fruta: $fruta"
    done
    

    Ejemplo 2: Contar elementos

    
    
    
    
    
    #!/bin/bash
    
    colores=("Rojo" "Azul" "Verde" "Amarillo")
    echo "Hay ${#colores[@]} colores en la lista."
    

    Ejemplo 3: Asociar países con capitales

    
    
    
    
    
    #!/bin/bash
    
    declare -A capitales
    capitales[España]="Madrid"
    capitales[Alemania]="Berlín"
    
    for pais in "${!capitales[@]}"; do
        echo "La capital de $pais es ${capitales[$pais]}"
    done
    

    Ejercicios propuestos

    Ejercicio 1:
    Crear un script frutas.sh con un array de frutas y recorrerlo con un for mostrando los elementos.

    Ejercicio 2:
    Crear un script contar.sh que almacene números del 10 al 50 en un array y muestre cuántos elementos contiene.

    Ejercicio 3:
    Crear un script capitales.sh con un array asociativo de países → capitales y luego imprimir:

    
    
    
    
    
    La capital de X es Y
    

    para cada país.

    Ejercicio 4:
    Crear un script archivos.sh que guarde en un array todos los archivos .txt del directorio actual y los liste.


    Soluciones

    ✅ Ejercicio 1 — frutas.sh

    Enunciado:
    Crear un script frutas.sh con un array de frutas y recorrerlo con un for mostrando los elementos.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: frutas.sh
    # Muestra una lista de frutas usando un array
    
    frutas=("Manzana" "Pera" "Naranja" "Plátano" "Kiwi")
    
    echo "Listado de frutas:"
    for fruta in "${frutas[@]}"; do
        echo " - $fruta"
    done
    

    Salida esperada:

    
    
    
    
    
    Listado de frutas:
     - Manzana
     - Pera
     - Naranja
     - Plátano
     - Kiwi
    

    ✅ Ejercicio 2 — contar.sh

    Enunciado:
    Crear un script contar.sh que almacene números del 10 al 50 en un array y muestre cuántos elementos contiene.

    Puedes interpretarlo de varias formas. Te dejo una solución clara y didáctica.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: contar.sh
    # Guarda números del 10 al 50 en un array y muestra cuántos hay
    
    numeros=()
    
    for ((i=10; i<=50; i++)); do
        numeros+=("$i")
    done
    
    echo "Contenido del array:"
    echo "${numeros[@]}"
    
    echo
    echo "El array contiene ${#numeros[@]} elementos."
    

    Salida (resumen):

    
    
    
    
    
    Contenido del array:
    10 11 12 ... 50
    
    El array contiene 41 elementos.
    

    ✅ Ejercicio 3 — capitales.sh

    Enunciado:
    Crear un script capitales.sh con un array asociativo de países → capitales y luego imprimir:

    La capital de X es Y

    para cada país.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: capitales.sh
    # Usa un array asociativo para países y capitales
    
    declare -A capitales
    
    capitales[España]="Madrid"
    capitales[Francia]="París"
    capitales[Alemania]="Berlín"
    capitales[Italia]="Roma"
    capitales[Portugal]="Lisboa"
    
    for pais in "${!capitales[@]}"; do
        echo "La capital de $pais es ${capitales[$pais]}"
    done
    

    Salida posible (el orden puede variar, porque los asociativos no garantizan orden):

    
    
    
    
    
    La capital de España es Madrid
    La capital de Francia es París
    La capital de Alemania es Berlín
    La capital de Italia es Roma
    La capital de Portugal es Lisboa
    

    ✅ Ejercicio 4 — archivos.sh

    Enunciado:
    Crear un script archivos.sh que guarde en un array todos los archivos .txt del directorio actual y los liste.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: archivos.sh
    # Guarda en un array los .txt del directorio actual y los muestra
    
    txt_files=(*.txt)
    
    # Comprobamos si hay alguno
    if [ "${#txt_files[@]}" -eq 1 ] && [ "${txt_files[0]}" = "*.txt" ]; then
        echo "No se han encontrado archivos .txt en el directorio actual."
        exit 0
    fi
    
    echo "Archivos .txt encontrados:"
    for fichero in "${txt_files[@]}"; do
        echo " - $fichero"
    done
    
    echo
    echo "Total de archivos .txt: ${#txt_files[@]}"
    

    Notas:

    • El truco de comprobar si el único elemento es "*.txt" evita el caso en que no haya .txt y el patrón no se expanda.
    • Esto da pie a hablar de globbing y de nullglob en niveles más avanzados.

  • 7. Funciones y Modularización del Código

    Los scripts que has hecho hasta ahora funcionan bien, pero tienen un problema: si el código crece, se convierte en algo difícil de mantener, leer y modificar. Las funciones solucionan esto al permitir reutilizar código y dividirlo en partes más pequeñas.

    Este módulo te enseñará a definir funciones, pasar argumentos, devolver valores y modularizar scripts más complejos.


    1. ¿Qué es una función en Bash?

    Una función es un bloque de código con nombre propio que puedes ejecutar cuando quieras, tantas veces como quieras.

    En pseudolenguaje:

    
    
    
    
    
    definir función → hacer algo
    llamar función → ejecuta ese algo
    

    2. Sintaxis para definir funciones

    Bash acepta dos sintaxis equivalentes:

    Forma 1:

    
    
    
    
    
    nombre_funcion() {
        comandos
    }
    

    Forma 2:

    
    
    
    
    
    function nombre_funcion {
        comandos
    }
    

    Ambas son válidas. Lo más usado es la primera por ser más limpia.


    3. Llamar a una función

    Una vez definida, basta con usar su nombre:

    
    
    
    
    
    mi_funcion
    

    Ejemplo:

    
    
    
    
    
    saludar() {
        echo "Hola desde la función"
    }
    
    saludar
    

    Salida:

    
    
    
    
    
    Hola desde la función
    

    4. Funciones con parámetros

    Las funciones pueden recibir argumentos igual que el script completo.

    Ejemplo:

    
    
    
    
    
    saludar() {
        echo "Hola $1"
    }
    
    saludar "Laura"
    

    Salida:

    
    
    
    
    
    Hola Laura
    

    Si deseas más parámetros:

    • $1 → primer argumento
    • $2 → segundo
    • $@ → todos

    5. Funciones que devuelven valores

    En Bash una función no “devuelve” valores como en otros lenguajes. Lo más común es:

    • Imprimir en pantalla
    • Guardar en variables
    • O usar return solo para códigos numéricos (0–255)

    5.1 Método estándar (captura por sustitución)

    
    
    
    
    
    sumar() {
        echo $(( $1 + $2 ))
    }
    
    resultado=$(sumar 3 4)
    echo "Resultado: $resultado"
    

    Salida:

    
    
    
    
    
    Resultado: 7
    

    5.2 return (solo numérico)

    
    
    
    
    
    comparar() {
        if [ $1 -gt $2 ]; then
            return 0
        else
            return 1
        fi
    }
    
    comparar 10 5
    
    if [ $? -eq 0 ]; then
        echo "El primero es mayor"
    fi
    

    6. Variables locales vs globales

    Por defecto, las variables en Bash son globales dentro del script.

    Para limitar su alcance dentro de la función, usa local:

    
    
    
    
    
    prueba() {
        local mensaje="Hola"
        echo "$mensaje"
    }
    
    prueba
    echo "$mensaje"  # Está vacía fuera de la función
    

    7. Organización modular del código

    A partir de ahora puedes:

    • Definir varias funciones en un mismo script
    • Usar funciones como “bloques” lógicos
    • Separar responsabilidades

    Ejemplo de script modular:

    
    
    
    
    
    mostrar_menu() {
        echo "1) Fecha"
        echo "2) Usuario"
        echo "3) Salir"
    }
    
    procesar_opcion() {
        case $1 in
            1) date ;;
            2) echo $USER ;;
            3) exit ;;
            *) echo "Opción inválida" ;;
        esac
    }
    
    mostrar_menu
    read -p "Opción: " op
    procesar_opcion $op
    

    8. Incluir funciones desde otros archivos (source)

    Esto permite crear “librerías” de funciones.

    Por ejemplo, en utils.sh:

    
    
    
    
    
    saludar() {
        echo "Hola $1"
    }
    

    En otro script:

    
    
    
    
    
    source utils.sh
    saludar "Carlos"
    

    Esto se usa para:

    • reutilizar código
    • crear módulos de funciones
    • evitar duplicar scripts

    9. Ejemplos

    Ejemplo 1: Función suma

    
    
    
    
    
    sumar() {
        echo $(( $1 + $2 ))
    }
    
    resultado=$(sumar 5 3)
    echo "Resultado: $resultado"
    

    Ejemplo 2: Librería simple

    log.sh:

    
    
    
    
    
    log() {
        echo "[LOG] $1"
    }
    

    test.sh:

    
    
    
    
    
    source log.sh
    log "Esto es una prueba"
    

    Ejemplo 3: Comprobador de archivos

    
    
    
    
    
    existe() {
        if [ -f "$1" ]; then
            echo "El archivo existe"
        else
            echo "No existe"
        fi
    }
    
    existe "/etc/passwd"
    

    Ejercicios propuestos

    Ejercicio 1:
    Crear un script matematicas.sh con funciones:

    • sumar
    • restar

    Cada función recibe 2 números y muestra el resultado.

    Ejercicio 2:
    Crear un script saludos.sh con una función saludar que reciba un nombre y muestre:

    
    
    
    
    
    Hola, <nombre>
    

    Ejercicio 3:
    Crear un script menu.sh con una función menu que muestre:

    1. Mostrar fecha
    2. Mostrar usuario
    3. Salir

    Y llame a otras funciones según la opción.

    Ejercicio 4:
    Crear un archivo utilidades.sh con una función log que agregue un mensaje a un archivo registro.txt.
    Luego crear un script test.sh que llame a log usando source.

    Soluciones

    ✅ Ejercicio 1 — matematicas.sh

    Enunciado:
    Crear un script matematicas.sh con funciones:

    • sumar
    • restar

    Cada función recibe 2 números y muestra el resultado.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: matematicas.sh
    # Funciones para sumar y restar dos números
    
    sumar() {
        # $1 y $2 son los parámetros de la función
        resultado=$(( $1 + $2 ))
        echo "La suma de $1 + $2 es $resultado"
    }
    
    restar() {
        resultado=$(( $1 - $2 ))
        echo "La resta de $1 - $2 es $resultado"
    }
    
    # Ejemplos de uso (se pueden sustituir por lectura de parámetros)
    sumar 10 5
    restar 10 5
    

    Si quieres que usen parámetros del script en lugar de valores fijos:

    
    
    
    
    
    #!/bin/bash
    
    sumar() {
        echo "La suma de $1 + $2 es $(( $1 + $2 ))"
    }
    
    restar() {
        echo "La resta de $1 - $2 es $(( $1 - $2 ))"
    }
    
    sumar "$1" "$2"
    restar "$1" "$2"
    

    ✅ Ejercicio 2 — saludos.sh

    Enunciado:
    Crear un script saludos.sh con una función saludar que reciba un nombre y muestre:

    Hola, <nombre>

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: saludos.sh
    # Función saludar que recibe un nombre
    
    saludar() {
        nombre="$1"
        echo "Hola, $nombre"
    }
    
    # Ejemplo 1: pasar el nombre como parámetro al script
    # saludar "$1"
    
    # Ejemplo 2: pedirlo por teclado
    read -p "Introduce tu nombre: " nom
    saludar "$nom"
    

    ✅ Ejercicio 3 — menu.sh

    Enunciado:
    Crear un script menu.sh con una función menu que muestre:

    1. Mostrar fecha
    2. Mostrar usuario
    3. Salir

    Y llame a otras funciones según la opción.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: menu.sh
    # Menú con funciones para organizar el código
    
    mostrar_fecha() {
        echo "Fecha actual: $(date +"%Y-%m-%d %H:%M:%S")"
    }
    
    mostrar_usuario() {
        echo "Usuario actual: $USER"
    }
    
    menu() {
        echo "==== MENÚ ===="
        echo "1) Mostrar fecha"
        echo "2) Mostrar usuario"
        echo "3) Salir"
    }
    
    # Bucle para que el menú se repita hasta salir
    while true; do
        menu
        read -p "Elige una opción: " opcion
    
        case "$opcion" in
            1) mostrar_fecha ;;
            2) mostrar_usuario ;;
            3) echo "Saliendo..."; exit 0 ;;
            *) echo "Opción no válida" ;;
        esac
    
        echo # línea en blanco para separar
    done
    

    ✅ Ejercicio 4 — utilidades.sh + test.sh

    Enunciado:

    1. Crear un archivo utilidades.sh con una función log que agregue un mensaje a un archivo registro.txt.
    2. Crear un script test.sh que llame a log usando source.

    💡 utilidades.sh

    
    
    
    
    
    #!/bin/bash
    # Archivo: utilidades.sh
    # Contiene funciones reutilizables
    
    log() {
        mensaje="$1"
        fecha=$(date +"%Y-%m-%d %H:%M:%S")
        echo "[$fecha] $mensaje" >> registro.txt
    }
    

    Importante: este archivo no se ejecuta solo, se “incluye” desde otros scripts.


    💡 test.sh

    
    
    
    
    
    #!/bin/bash
    # Script: test.sh
    # Usa la función log de utilidades.sh
    
    # Cargar funciones desde utilidades.sh
    source ./utilidades.sh
    
    log "Inicio de la ejecución de test.sh"
    echo "He escrito una línea en registro.txt"
    
    # Puedes añadir más registros
    log "Otra acción importante realizada"
    

    Al ejecutar:

    
    
    
    
    
    chmod +x test.sh
    ./test.sh
    

    Contenido de registro.txt (ejemplo):

    
    
    
    
    
    [2026-01-11 19:30:01] Inicio de la ejecución de test.sh
    [2026-01-11 19:30:01] Otra acción importante realizada
    

  • 8. Entrada, Salida y Redirecciones

    En Bash, la forma en la que controlamos la entrada (lo que llega al programa) y la salida (lo que produce el programa) es crucial para automatizar tareas, guardar resultados, procesar archivos y comunicarnos entre procesos.

    Este módulo te enseña a usar redirecciones, pipes, flujos estándar y here documents, herramientas que convierten comandos simples en utilidades potentes.


    1. Los tres flujos estándar

    Todo comando tiene tres “canales”:

    FlujoNombreDescriptorSignificado
    stdinentrada estándar0Lo que el programa lee
    stdoutsalida estándar1Salida normal del programa
    stderrsalida de errores2Mensajes de error

    Ejemplo mental:

    • stdin: lo que escribes con el teclado
    • stdout: lo que ves normalmente en pantalla
    • stderr: errores que también aparecen en pantalla, pero son distintos

    2. Redirección de Salida (> y >>)

    Permite enviar la salida a un archivo en lugar de la pantalla.

    2.1 Sobrescribir (>)

    
    
    
    
    
    ls > archivos.txt
    

    Crea archivos.txt con la salida de ls.

    Si ya existía, lo sobrescribe.


    2.2 Añadir (>>)

    
    
    
    
    
    date >> registros.txt
    

    Añade la fecha al final sin borrar lo anterior.


    3. Redirección de Errores (2>)

    Para redirigir errores (stderr):

    
    
    
    
    
    comando 2> errores.log
    

    Ejemplo típico:

    
    
    
    
    
    ls /no/existe 2> errores.log
    

    errores.log contendrá:

    
    
    
    
    
    ls: no se puede acceder a '/no/existe': No existe el archivo o el directorio
    

    4. Redirección combinada (&>, 2>&1)

    A veces queremos juntar stdout + stderr.

    Hay dos métodos:

    4.1 Enviar ambos a un archivo

    
    
    
    
    
    comando &> salida.log
    

    4.2 Enviar stderr → stdout

    
    
    
    
    
    comando > salida.log 2>&1
    

    Eso significa:

    • > salida.log → stdout va a salida.log
    • 2>&1 → stderr va al mismo sitio que stdout

    5. Redirección de entrada (<)

    Permite que un comando lea datos desde un archivo.

    Ejemplo:

    
    
    
    
    
    sort < notas.txt
    

    Esto ordena las líneas de notas.txt y las muestra por pantalla.


    6. Pipes (|)

    Los pipes conectan programas entre sí: la salida de uno es la entrada del siguiente.

    Formato:

    
    
    
    
    
    comando1 | comando2
    

    Ejemplo clásico:

    
    
    
    
    
    ls /etc | grep ssh
    

    Interpretación:

    1. ls /etc lista todo
    2. grep ssh se queda con lo que contenga “ssh”

    Más ejemplos útiles:

    • Ver procesos y filtrar:
    
    
    
    
    
    ps aux | grep firefox
    
    • Contar líneas de un archivo:
    
    
    
    
    
    cat archivo.txt | wc -l
    
    • Buscar puertos en uso:
    
    
    
    
    
    netstat -tuln | grep ":80"
    

    7. Registro en archivos (logs)

    Para guardar salidas a un log sin perder errores:

    
    
    
    
    
    comando >> log.txt 2>&1
    

    Este patrón se usa muchísimo en servidores.


    8. Here Documents (<<)

    Permiten “pasar un texto” como entrada a un comando o script.

    Ejemplo:

    
    
    
    
    
    cat << EOF
    Hola mundo
    Esto es un here document
    EOF
    

    Salida:

    
    
    
    
    
    Hola mundo
    Esto es un here document
    

    Usos típicos: scripts SQL, correos, configuraciones, etc.

    Ejemplo con mysql:

    
    
    
    
    
    mysql -u root -p << EOF
    SHOW DATABASES;
    EOF
    

    9. Here Strings (<<<)

    Son como here documents pero para una sola cadena.

    Ejemplo:

    
    
    
    
    
    grep texto <<< "hola texto adiós"
    

    Resultado:

    
    
    
    
    
    hola texto adiós
    

    10. Ejemplos

    Ejemplo 1: Guardar un listado

    
    
    
    
    
    ls -l > listado.txt
    

    Ejemplo 2: Capturar errores

    
    
    
    
    
    find / -name "*.conf" 2> errores.txt
    

    Ejemplo 3: Recuento de procesos

    
    
    
    
    
    ps aux | wc -l
    

    Ejemplo 4: Ordenar archivo y guardar

    
    
    
    
    
    sort < nombres.txt > nombres_ordenados.txt
    

    Ejemplo 5: Script que registra salida y errores

    
    
    
    
    
    ./backup.sh >> backup.log 2>&1
    

    Ejercicios Propuestos

    Ejercicio 1:
    Crear un script lista.sh que liste /etc y guarde la salida en etc.txt.

    Ejercicio 2:
    Crear un script errores.sh que haga ls a un directorio que no existe y guarde el error en errores.log.

    Ejercicio 3:
    Crear un script red.sh que use un pipe para mostrar solo las líneas de ifconfig que contengan inet.

    Ejercicio 4:
    Crear un script ordenar.sh que ordene las líneas de ciudades.txt y las guarde en ordenadas.txt.

    Soluciones

    Ejercicio 1 — lista.sh

    Enunciado:
    Crear un script lista.sh que liste /etc y guarde la salida en etc.txt.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: lista.sh
    # Lista el contenido de /etc y lo guarda en etc.txt
    
    ls /etc > etc.txt
    echo "Listado guardado en etc.txt"
    

    Tras ejecutarlo:

    
    
    
    
    
    ./lista.sh
    

    etc.txt contendrá el listado del directorio /etc.


    Ejercicio 2 — errores.sh

    Enunciado:
    Crear un script errores.sh que haga ls a un directorio que no existe y guarde el error en errores.log.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: errores.sh
    # Genera un error de ls y lo guarda en errores.log
    
    ls /ruta/que/no/existe 2> errores.log
    echo "Error guardado en errores.log"
    

    Después de ejecutarlo:

    
    
    
    
    
    cat errores.log
    

    Salida esperada (puede variar según el idioma del sistema):

    
    
    
    
    
    ls: no se puede acceder a '/ruta/que/no/existe': No existe el archivo o el directorio
    

    Ejercicio 3 — red.sh

    Enunciado:
    Crear un script red.sh que use un pipe para mostrar solo las líneas de ifconfig que contengan inet.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: red.sh
    # Muestra solo las líneas con inet usando pipes
    
    ifconfig | grep inet
    

    Si usas ip a (más moderno que ifconfig), sería:

    
    
    
    
    
    #!/bin/bash
    ip a | grep inet
    

    Salida típica:

    
    
    
    
    
    inet 192.168.1.100/24 brd 192.168.1.255 scope global wlan0
    inet6 fe80::abcd:1234:5678:9101/64 scope link
    

    Ejercicio 4 — ordenar.sh

    Enunciado:
    Crear un script ordenar.sh que ordene las líneas de ciudades.txt y las guarde en ordenadas.txt.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: ordenar.sh
    # Ordena el archivo ciudades.txt y guarda el resultado
    
    sort < ciudades.txt > ordenadas.txt
    echo "Archivo ordenado guardado en ordenadas.txt"
    

    Si ciudades.txt contiene:

    
    
    
    
    
    Madrid
    Sevilla
    Barcelona
    Valencia
    

    El resultado en ordenadas.txt será:

    
    
    
    
    
    Barcelona
    Madrid
    Sevilla
    Valencia
    

    Otra opción

    Si quieres que los alumnos vean prácticas reales:

    Guardar logs sin machacar:

    
    
    
    
    
    ls /etc >> etc.log 2>&1
    

    Redirigir stdout + stderr a la vez:

    
    
    
    
    
    comando >> salida.log 2>&1
    

    Ejemplo realista: backup con logging:

    
    
    
    
    
    ./backup.sh >> backup.log 2>&1
    

    Esto se usa en cronjobs y servicios systemd.


    Resumen de herramientas usadas en los ejercicios

    HerramientaUso
    >redirigir salida, sobrescribe
    >>redirigir salida, añade
    2>redirigir errores
    ``
    <redirigir entrada desde archivo
    sortordenar líneas
    grepfiltrar texto

  • 9. Gestión de Archivos y Automatización del Sistema

    La automatización en Bash suele implicar mover, copiar, buscar, procesar y vigilar archivos, además de ejecutar tareas periódicas. En este módulo aprenderás a manipular archivos desde scripts y a automatizar acciones sobre el sistema, entrando ya en terreno de administrador serio.


    1. Comandos esenciales para gestionar archivos

    Bash no inventa nada aquí: usa comandos del sistema. Estos son tus martillos y destornilladores:

    ComandoUso
    cpCopiar archivos o directorios
    mvMover o renombrar
    rmBorrar archivos
    mkdirCrear directorios
    rmdirBorrar directorios vacíos
    touchCrear archivos vacíos o actualizar fecha
    chmodCambiar permisos
    chownCambiar propietario
    findBuscar archivos
    fileDetectar tipo de archivo

    Ejemplos rápidos:

    
    
    
    
    
    cp origen.txt destino.txt
    mv archivo.txt /tmp/
    rm viejo.txt
    mkdir respaldos
    touch notas.log
    

    2. Copias de seguridad (Backups) simples

    Un backup básico es copiar archivos o carpetas a otro lugar. Ejemplo:

    
    
    
    
    
    #!/bin/bash
    cp -r /home/usuario/documentos /home/usuario/respaldo/
    

    Añadir fecha para evitar sobreescritura:

    
    
    
    
    
    fecha=$(date +%Y-%m-%d)
    cp -r /home/usuario/documentos "/home/usuario/respaldo/doc_$fecha"
    

    Así tendrás:

    
    
    
    
    
    doc_2026-01-12/
    doc_2026-01-13/
    doc_2026-01-14/
    ...
    

    Automatización empieza aquí.


    3. Comprimir y descomprimir desde scripts

    Común en backups, logs y despliegue de software.

    HerramientaUso
    tarEmpaquetar/comprimir
    gzCompresión gzip
    zip/unzipCompresión zip

    Ejemplo:

    
    
    
    
    
    tar -czf respaldo.tar.gz /etc
    

    Explicación:

    • c → crear
    • z → comprimir con gzip
    • f → indicar nombre del archivo

    Descomprimir:

    
    
    
    
    
    tar -xzf respaldo.tar.gz
    

    4. Buscar archivos con find

    find es un radar. Busca por nombre, tamaño, fecha, permisos…

    Ejemplos útiles:

    Encontrar archivos por nombre:

    
    
    
    
    
    find /var/log -name "*.log"
    

    Por tamaño:

    
    
    
    
    
    find / -size +100M
    

    Por fecha (modificados hace menos de 1 día):

    
    
    
    
    
    find /tmp -mtime -1
    

    Por permisos inseguros:

    
    
    
    
    
    find /home -perm 777
    

    Puedes además ejecutar acciones:

    
    
    
    
    
    find /var/log -name "*.log" -exec gzip {} \;
    

    Esto comprime todos los .log.


    5. Analizar y procesar logs

    Los logs cuentan la historia del sistema. Para analizarlos usamos:

    • grep (buscar patrones)
    • cut (extraer columnas)
    • awk (procesar datos)
    • sed (modificar texto)

    Ejemplos básicos:

    Buscar errores:

    
    
    
    
    
    grep -i "error" /var/log/syslog
    

    Buscar IPs:

    
    
    
    
    
    grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" acceso.log
    

    Contar líneas:

    
    
    
    
    
    wc -l /var/log/auth.log
    

    6. Automatización con cron

    cron ejecuta tareas programadas automáticamente.

    Editar tareas del usuario:

    
    
    
    
    
    crontab -e
    

    Formato:

    
    
    
    
    
    MIN HORA DIA MES DIASEM EJECUTABLE
    

    Ejemplos:

    Ejecutar un script cada día a las 23:00:

    
    
    
    
    
    0 23 * * * /home/user/scripts/backup.sh
    

    Cada 5 minutos:

    
    
    
    
    
    */5 * * * * /home/user/scripts/monitor.sh
    

    Ver tareas activas:

    
    
    
    
    
    crontab -l
    

    7. Automatización con systemd (modo pro)

    systemd permite ejecutar servicios y temporizadores.

    Estructura:

    • .service → define una acción
    • .timer → define cuándo se ejecuta

    Ejemplo de servicio:

    /etc/systemd/system/backup.service

    
    
    
    
    
    [Unit]
    Description=Backup Diario
    
    [Service]
    ExecStart=/usr/local/bin/backup.sh
    

    Ejemplo de timer:

    /etc/systemd/system/backup.timer

    
    
    
    
    
    [Unit]
    Description=Programa el backup diario
    
    [Timer]
    OnCalendar=daily
    
    [Install]
    WantedBy=timers.target
    

    Activar timer:

    
    
    
    
    
    systemctl enable --now backup.timer
    

    Ver logs:

    
    
    
    
    
    journalctl -u backup.service
    

    Los alumnos pueden ver cómo se automatiza un servidor real.


    8. Ejemplos

    Ejemplo 1: Backup con timestamp

    
    
    
    
    
    #!/bin/bash
    fecha=$(date +%Y-%m-%d_%H-%M)
    tar -czf backup_$fecha.tar.gz /home/alumno
    

    Ejemplo 2: Buscar archivos grandes

    
    
    
    
    
    #!/bin/bash
    find / -size +500M > grandes.txt
    

    Ejemplo 3: Comprimir logs viejos

    
    
    
    
    
    #!/bin/bash
    find /var/log -name "*.log" -mtime +7 -exec gzip {} \;
    

    Ejemplo 4: Filtrar errores de syslog

    
    
    
    
    
    #!/bin/bash
    grep -i "error" /var/log/syslog > errores.log
    

    Ejercicios Propuestos

    Ejercicio 1:
    Crear un script backup.sh que comprima /etc en un archivo etc.tar.gz.

    Ejercicio 2:
    Crear un script buscar.sh que busque archivos .conf en /etc y guarde la lista en conf.txt.

    Ejercicio 3:
    Crear un script errores.sh que filtre la palabra “error” del archivo /var/log/syslog y lo guarde en errores.log.

    Ejercicio 4:
    Crear una entrada cron que ejecute backup.sh todos los días a las 23:00.

    Ejercicio 5 (opcional pro):
    Crear un .service + .timer para ejecutar buscar.sh cada hora.


    Soluciones

    ✅ Ejercicio 1 — backup.sh

    Enunciado:
    Crear un script backup.sh que comprima /etc en un archivo etc.tar.gz.

    💡 Solución básica:

    
    
    
    
    
    #!/bin/bash
    # Script: backup.sh
    # Crea un backup comprimido del directorio /etc
    
    tar -czf etc.tar.gz /etc
    echo "Backup creado: etc.tar.gz"
    

    💡 Solución un poco mejor (con fecha):

    
    
    
    
    
    #!/bin/bash
    # Script: backup.sh
    # Crea un backup comprimido del directorio /etc con fecha
    
    fecha=$(date +%Y-%m-%d_%H-%M)
    archivo="etc_$fecha.tar.gz"
    
    tar -czf "$archivo" /etc
    echo "Backup creado: $archivo"
    

    ✅ Ejercicio 2 — buscar.sh

    Enunciado:
    Crear un script buscar.sh que busque archivos .conf en /etc y guarde la lista en conf.txt.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: buscar.sh
    # Busca archivos .conf en /etc y los guarda en conf.txt
    
    find /etc -type f -name "*.conf" > conf.txt
    echo "Listado de archivos .conf guardado en conf.txt"
    

    Si quieres que añada en lugar de sobrescribir:

    
    
    
    
    
    find /etc -type f -name "*.conf" >> conf.txt
    

    ✅ Ejercicio 3 — errores.sh

    Enunciado:
    Crear un script errores.sh que filtre la palabra “error” del archivo /var/log/syslog y lo guarde en errores.log.

    💡 Solución:

    
    
    
    
    
    #!/bin/bash
    # Script: errores.sh
    # Filtra líneas con 'error' en /var/log/syslog y las guarda en errores.log
    
    grep -i "error" /var/log/syslog > errores.log
    echo "Líneas con 'error' guardadas en errores.log"
    
    • -i → ignora mayúsculas/minúsculas (ERROR, Error, error…).

    ✅ Ejercicio 4 — Entrada de cron

    Enunciado:
    Crear una entrada cron que ejecute backup.sh todos los días a las 23:00.

    💡 Suposiciones:

    • El script está en: /home/usuario/scripts/backup.sh
    • Tiene permisos de ejecución: chmod +x backup.sh

    💡 Línea de crontab:

    Se edita con:

    
    
    
    
    
    crontab -e
    

    Y se añade:

    
    
    
    
    
    0 23 * * * /home/usuario/scripts/backup.sh >> /home/usuario/scripts/backup.log 2>&1
    

    Explicación rápida:

    • 0 23 * * * → a las 23:00 todos los días
    • >> backup.log 2>&1 → guarda salida y errores en un log (estilo “vida real”)

    ✅ Ejercicio 5 (opcional) — systemd service + timer

    Enunciado:
    Crear un .service + .timer para ejecutar buscar.sh cada hora.

    1️⃣ Script buscar.sh (por si acaso):

    
    
    
    
    
    #!/bin/bash
    # /usr/local/bin/buscar.sh
    
    find /etc -type f -name "*.conf" > /var/log/conf.txt
    

    Dar permisos:

    
    
    
    
    
    sudo chmod +x /usr/local/bin/buscar.sh
    

    2️⃣ Unidad de servicio: /etc/systemd/system/buscar.service

    
    
    
    
    
    [Unit]
    Description=Buscar archivos .conf en /etc
    
    [Service]
    Type=oneshot
    ExecStart=/usr/local/bin/buscar.sh
    

    3️⃣ Unidad de temporizador: /etc/systemd/system/buscar.timer

    
    
    
    
    
    [Unit]
    Description=Ejecutar buscar.sh cada hora
    
    [Timer]
    OnCalendar=hourly
    Persistent=true
    
    [Install]
    WantedBy=timers.target
    

    4️⃣ Activar y comprobar

    Recargar unidades:

    
    
    
    
    
    sudo systemctl daemon-reload
    

    Activar y arrancar el timer:

    
    
    
    
    
    sudo systemctl enable --now buscar.timer
    

    Ver estado:

    
    
    
    
    
    systemctl status buscar.timer
    

    Ver ejecuciones del servicio:

    
    
    
    
    
    journalctl -u buscar.service
    

  • 10. ¿QUÉ ES EL PATH?

    PATH es una variable de entorno que contiene una lista de directorios separados por dos puntos. Cuando escribes un comando como ls, el shell recorre esos directorios en orden buscando un ejecutable llamado “ls”. Si lo encuentra, lo ejecuta. Si no lo encuentra, error.

    Puedes verlo así:

    echo "$PATH"
    

    Ejemplo típico de salida:

    /usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/alumno/.local/bin

    El orden importa: el shell mira primero el primer directorio, luego el siguiente, y así.

    CREAR UN SCRIPT

    #!/bin/bash
    echo "Hola desde el sistema, $(whoami)!"
    

    Guardar como saludo.sh.

    –––––––

    ELEGIR DÓNDE VIVIRÁ
    Aquí hay dos caminos:

    A — Sólo para el usuario
    Usar ~/.local/bin (en muchas distros modernas este ya está en PATH).

    Si no existe:

    mkdir -p ~/.local/bin
    

    Luego mover el script ahí:

    mv saludo.sh ~/.local/bin/
    

    B — Para todo el sistema (requiere sudo)
    Usar /usr/local/bin:

    sudo mv saludo.sh /usr/local/bin/
    

    Esto hace que el script esté disponible para todos los usuarios

    –––––––

    PERMISOS
    El script debe tener el bit de ejecución activo. Si no:

    chmod +x ~/.local/bin/saludo.sh
    

    o si está en /usr/local/bin:

    sudo chmod +x /usr/local/bin/saludo.sh
    

    –––––––

    VERIFICAR SI EL DIRECTORIO YA ESTÁ EN EL PATH
    Para el caso ~/.local/bin, muchas distros lo incluyen. Comprobar con:

    echo "$PATH" | tr ':' '\n'
    

    Si se ve ~/.local/bin, listo.

    Si NO está, hay que añadirlo en ~/.bashrc o ~/.profile:

    echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
    source ~/.bashrc
    

    Aquí puedes introducir la distinción entre bashrc, profile y shells interactivos / no interactivos, pero sin entrar en guerra santa. A nivel pedagógico basta con que entiendan que esa línea hace persistente la ruta.

    –––––––

    EJECUTAR DESDE CUALQUIER PARTE
    Ve a cualquier carpeta:

    cd /tmp
    saludo.sh
    

    El script debería ejecutarse como cualquier comando del sistema. Si alguien intenta poner ./saludo.sh, puedes remarcar que ahora no hace falta, porque no necesita ruta explícita.

    –––––––

    OPCIONAL — QUITAR LA EXTENSIÓN
    Para que se sienta más “comando real”, renombrarlo:

    mv ~/.local/bin/saludo.sh ~/.local/bin/saludo
    

    Y ejecutarlo simplemente como:

    saludo
    

    La extensión .sh es cultural, no obligatoria, y que comandos del sistema no suelen llevar extensión.

  • 11. [Reto] Bash Incremental: Aprender Shell a Golpes de Ejemplos

    11. [Reto] Bash Incremental: Aprender Shell a Golpes de Ejemplos

    En esta práctica trabajarás con una colección de scripts en Bash orientados a tareas básicas de automatización y administración en Linux. El objetivo es que ejecutes y analices cada uno de los scripts en tu terminal para comprender su funcionamiento y el efecto que producen.

    Tareas a realizar

    1. Copia todos los scripts proporcionados en tu máquina Linux (física o virtual).
    2. Asegúrate de que cada script tenga permisos de ejecución usando: chmod +x nombre_script.sh
    3. Ejecuta cada script desde la terminal y observa el resultado.
    4. Modifica al menos 3 scripts para adaptarlos a una variante que tenga sentido. Por ejemplo:
      • Cambiar rutas
      • Cambiar comandos
      • Añadir opciones
      • Añadir colores o logs

    La línea del “shebang” (#!)

    #!/bin/bash
    #!/usr/bin/env bash

    La diferencia tiene que ver con flexibilidad vs. certeza.

    Cuando pones #!/bin/bash estás diciendo “usa Bash que está en /bin/bash”.
    Eso asume que la ruta es exactamente esa. En muchas distros Linux es verdad, en macOS a veces también, pero en otras no. Por ejemplo, en sistemas BSD, en instalaciones personalizadas, o si manejas múltiples versiones de Bash instaladas desde brew, nix, etc., /bin/bash puede no existir o no ser el Bash que quieres.

    La alternativa #!/usr/bin/env bash juega una carta distinta:
    env busca ejecutables en el PATH del usuario, encuentra dónde está bash y lo lanza.
    Es como decir: “búscame un Bash válido en la ruta del sistema y úsalo”.
    Eso lo vuelve más portátil y adaptable, sobre todo en entornos raros, macOS moderno, contenedores, virtual environments y setups educativos donde Bash no está donde uno espera.

    El resumen práctico:

    #!/bin/bash = certero pero rígido. Si no existe esa ruta, falla.
    #!/usr/bin/env bash = más portable y respetuoso del PATH, útil cuando no conoces el entorno.

    Ejemplo 1: “Hola, soy un script”
    Propósito: variables y echo.

    #!/usr/bin/env bash
    
    nombre="Antonio"
    echo "Hola, $nombre"
    echo "Estoy en el directorio: $(pwd)"
    

    Concepto nuevo: variables y comandos incrustados $(...).


    Ejemplo 2: “Si me pasas un nombre, lo uso”
    Propósito: argumentos $1.

    #!/usr/bin/env bash
    
    if [ -z "$1" ]; then
      echo "Uso: $0 <nombre>"
      exit 1
    fi
    
    echo "Hola, $1"
    

    Concepto nuevo: if mínimo, chequeo de argumentos y exit.


    Ejemplo 3: “Saludo a varios”
    Propósito: recorrer argumentos (for).

    #!/usr/bin/env bash
    
    if [ "$#" -eq 0 ]; then
      echo "Uso: $0 nombre1 nombre2 ..."
      exit 1
    fi
    
    for persona in "$@"; do
      echo "Hola, $persona"
    done
    

    Concepto nuevo: for y "$@" (todos los argumentos).


    Ejemplo 4: “Saludo según hora”
    Propósito: condicionales compuestos.

    #!/usr/bin/env bash
    
    hora=$(date +%H)
    
    if [ "$hora" -lt 12 ]; then
      saludo="Buenos días"
    elif [ "$hora" -lt 20 ]; then
      saludo="Buenas tardes"
    else
      saludo="Buenas noches"
    fi
    
    echo "$saludo, $(whoami)"
    

    Concepto nuevo: elif, comparaciones numéricas y uso de comandos para info del sistema.


    Pequeño ejemplo 5: “Función que loguea cosas”
    Propósito: funciones y reutilización.

    #!/usr/bin/env bash
    
    log(){
      echo "[$(date +%H:%M:%S)] $1"
    }
    
    log "Iniciando proceso"
    sleep 1
    log "Proceso terminado"
    

    Concepto nuevo: funciones y timestamps.


    Pequeño ejemplo 6: “Descargo URLs de una lista”
    Propósito: arrays, loops y llamar programas externos.

    Archivo urls.txt de ejemplo:

    https://example.org
    https://wikipedia.org
    

    Script:

    #!/usr/bin/env bash
    
    while read -r url; do
      [ -z "$url" ] && continue
      echo "Descargando $url"
      curl -s -O "$url"
    done < urls.txt
    

    Concepto nuevo: while read, redirección y pequeñas automatizaciones.


    Ejemplo 7: “Contador de líneas de código por extensión”
    Propósito: bucles, condicionales, comandos, pipes.

    #!/usr/bin/env bash
    
    if [ -z "$1" ]; then
      echo "Uso: $0 <directorio>"
      exit 1
    fi
    
    dir="$1"
    for ext in sh py js php; do
      total=$(find "$dir" -type f -name "*.$ext" -print0 | xargs -0 cat 2>/dev/null | wc -l)
      echo "$ext: $total líneas"
    done
    

    Concepto nuevo: usar herramientas externas y procesar resultados.


    Ejemplo 8: “Menú interactivo mínimo”
    Propósito: select y control básico de flujo.

    #!/usr/bin/env bash
    
    select opcion in Fecha Usuario Salir; do
      case "$opcion" in
        Fecha) date ;;
        Usuario) whoami ;;
        Salir) echo "Adiós"; break ;;
        *) echo "Opción no válida" ;;
      esac
    done
    

    Concepto nuevo: select + case.


    Pequeño ejemplo 9: “Backup comprimido con timestamp”
    Propósito: combinación de todo para algo útil.

    #!/usr/bin/env bash
    
    origen="$1"
    destino="$2"
    
    if [ -z "$origen" ] || [ -z "$destino" ]; then
      echo "Uso: $0 <directorio_origen> <directorio_destino>"
      exit 1
    fi
    
    fecha=$(date +%Y%m%d_%H%M%S)
    archivo="$destino/backup_$fecha.tar.gz"
    
    echo "Creando backup de $origen en $archivo..."
    tar -czf "$archivo" "$origen"
    echo "Backup creado."

    Ejemplo 10: Crear estructura de carpetas de proyecto

    Idea: Montar la estructura típica de un proyecto sin ir haciendo mkdir a mano.

    #!/usr/bin/env bash
    
    if [ -z "$1" ]; then
      echo "Uso: $0 <nombre_proyecto>"
      exit 1
    fi
    
    PROYECTO="$1"
    
    mkdir -p "$PROYECTO"/{src,bin,logs,tmp,docs}
    
    echo "Proyecto creado en: $PROYECTO"
    ls -R "$PROYECTO"
    

    Ejemplo 11: Buscar una palabra en todos los .log de un directorio

    Idea: Mini-grep para revisar logs del sistema o de una app.

    #!/usr/bin/env bash
    
    DIR="${1:-/var/log}"
    CADENA="$2"
    
    if [ -z "$CADENA" ]; then
      echo "Uso: $0 <directorio_logs> <cadena_a_buscar>"
      echo "Ejemplo: $0 /var/log error"
      exit 1
    fi
    
    echo "Buscando '$CADENA' en *.log de $DIR..."
    grep -Rni --include="*.log" "$CADENA" "$DIR"
    

    Ejemplo 12: Top 5 archivos más grandes de un directorio

    Idea: Visualizar qué archivos se están comiendo el disco.

    #!/usr/bin/env bash
    
    DIR="${1:-.}"
    
    echo "Top 5 archivos más grandes en: $DIR"
    du -ah "$DIR" 2>/dev/null | sort -h | tail -n 5
    

    Ejemplo 13: Aviso sencillo de uso de disco

    Idea: Script que comprueba / y avisa si pasa de cierto porcentaje.

    #!/usr/bin/env bash
    
    LIMITE=80  # porcentaje
    
    uso=$(df -h / | awk 'NR==2 {gsub("%","",$5); print $5}')
    
    echo "Uso actual de / : $uso%"
    
    if [ "$uso" -gt "$LIMITE" ]; then
      echo "⚠ Atención: uso de disco por encima de ${LIMITE}%"
    else
      echo "Todo OK, por debajo de ${LIMITE}%"
    fi
    

    Ejemplo 14: Copia rápida de syslog con timestamp

    Idea: Guardar copia de seguridad de un log del sistema en el HOME del usuario.

    #!/usr/bin/env bash
    
    ORIGEN="/var/log/syslog"
    DEST="$HOME"
    
    if [ ! -f "$ORIGEN" ]; then
      echo "No existe $ORIGEN (en algunas distros es /var/log/messages)"
      exit 1
    fi
    
    FECHA=$(date +%Y%m%d_%H%M%S)
    COPIA="$DEST/syslog_$FECHA.log"
    
    cp "$ORIGEN" "$COPIA"
    
    echo "Copia creada en: $COPIA"
    

    Ejemplo 15: Ping-monitor cutre pero efectivo

    Idea: Comprobar conectividad a un host cada pocos segundos.

    #!/usr/bin/env bash
    
    HOST="${1:-8.8.8.8}"
    
    echo "Monitoreando ping a $HOST (Ctrl+C para salir)"
    
    while true; do
      if ping -c 1 -W 1 "$HOST" >/dev/null 2>&1; then
        echo "[$(date +%H:%M:%S)] $HOST está accesible"
      else
        echo "[$(date +%H:%M:%S)] ⚠ $HOST NO responde"
      fi
      sleep 3
    done
    

    Ejemplo 16: Listar usuarios del sistema con su shell

    Idea: Leer /etc/passwd y mostrar usuario + shell. Tocado sistema pero muy didáctico.

    #!/usr/bin/env bash
    
    echo "Usuarios del sistema y su shell:"
    echo "--------------------------------"
    
    while IFS=: read -r user _ _ _ _ _ shell; do
      echo "$user -> $shell"
    done < /etc/passwd
    

    Ejemplo 17: Comprobar si un servicio está activo (systemd)

    Idea: Ver el estado de un servicio con systemctl.

    #!/usr/bin/env bash
    
    if [ -z "$1" ]; then
      echo "Uso: $0 <nombre_servicio>"
      echo "Ejemplo: $0 apache2"
      exit 1
    fi
    
    SERV="$1"
    
    estado=$(systemctl is-active "$SERV" 2>/dev/null)
    
    if [ "$estado" = "active" ]; then
      echo "✅ El servicio $SERV está ACTIVO"
    elif [ "$estado" = "inactive" ]; then
      echo "⏸ El servicio $SERV está INACTIVO"
    else
      echo "❓ El servicio $SERV no existe o no usa systemd (estado: $estado)"
    fi
    

    Ejemplo 18: Renombrar archivos añadiendo un prefijo

    Idea: Jugar con nombres de archivos y bucles. Muy útil para fotos, prácticas, etc.

    #!/usr/bin/env bash
    
    if [ "$#" -lt 2 ]; then
      echo "Uso: $0 <prefijo> <archivos...>"
      echo "Ejemplo: $0 practica1 *.txt"
      exit 1
    fi
    
    PREFIJO="$1"
    shift
    
    for fichero in "$@"; do
      if [ -f "$fichero" ]; then
        nuevo="${PREFIJO}_$fichero"
        mv "$fichero" "$nuevo"
        echo "$fichero -> $nuevo"
      else
        echo "Saltando $fichero (no es un archivo)"
      fi
    done
    

    Ejemplo 19: Menú para ver info básica del sistema

    Idea: Un menú simple que llame a varios comandos de sistema.

    #!/usr/bin/env bash
    
    mostrar_sistema() {
      echo "Sistema:"
      uname -a
    }
    
    mostrar_cpu() {
      echo "CPU:"
      lscpu | head -n 10
    }
    
    mostrar_mem() {
      echo "Memoria:"
      free -h
    }
    
    mostrar_disks() {
      echo "Discos:"
      df -h
    }
    
    PS3="Elige una opción (1-5): "
    
    select opcion in "Info sistema" "CPU" "Memoria" "Discos" "Salir"; do
      case "$REPLY" in
        1) mostrar_sistema ;;
        2) mostrar_cpu ;;
        3) mostrar_mem ;;
        4) mostrar_disks ;;
        5) echo "Adiós"; break ;;
        *) echo "Opción no válida" ;;
      esac
    done
    

  • Actividad – Completa Script de NMAP

    Actividad – Completa Script de NMAP

    Hacer un script de Bash que reúna los comandos y opciones más útiles de nmap, organizado en funciones y con un menú para que practiquen if/else, funciones, lectura de parámetros y buenas prácticas.y

    Completa el script, para ello se te propociona el menú y el conjunto de funciones utilizadas. Debes completarlo para que tenga un funcionamiento correcto

    # Menú principal
    main_menu() {
    
      while true; do
        clear
        echo "========================================"
        echo "       NMAP HELPER - MENÚ EDUCATIVO     "
        echo "========================================"
        echo "1) Ping scan (descubrir hosts vivos)"
        echo "2) TCP SYN top 100 (--top-ports 100 -sS)"
        echo "3) TCP connect scan y puertos personalizados (-sT -p)"
        echo "4) Detección de servicios y versiones (-sV -sC)"
        echo "5) Detección de SO (-O)"
        echo "6) Escaneo UDP (-sU)"
        echo "7) Escaneo agresivo (-A)"
        echo "8) NSE scripts (--script)"
        echo "9) Salida en formatos (-oN, -oX, -oG)"
        echo "10) Escaneo sin ping (-Pn)"
        echo "11) Crea tu propia opción para namp"
        echo "12) Salir"
        echo "========================================"
        read -p "Elige opción [1-12]: " opt

    En todas las opciones al final debe dar el mensaje de legalidad de uso.

    usage_note() {
      cat <<EOF
    
    IMPORTANTE (ÉTICA Y LEGAL):
    - Solo escanea equipos y redes que sean de tu propiedad o para los que tengas permiso explícito.
    - El escaneo puede ser detectado por IDS/IPS y puede tener consecuencias en redes de producción.
    - No uses estos comandos en redes públicas sin autorización.
    
    EOF
    }

  • Actividad – Script interactivo para gestión de red con Netplan

    Actividad – Script interactivo para gestión de red con Netplan


    En entornos Linux de servidor, la configuración de red es un elemento crítico. Un error puede dejar el sistema incomunicado, inaccesible por SSH o fuera de la red. En Ubuntu Server moderno, la gestión de red se realiza mediante Netplan, que utiliza archivos YAML para definir cómo se configuran las interfaces.

    En administraciones reales, cambiar entre IP dinámica (DHCP) y IP estática es una tarea habitual:

    • Laboratorios → DHCP automático
    • Servidores → IP fija y controlada
    • Máquinas virtuales → cambios frecuentes
    • Entornos de pruebas → configuraciones rápidas

    Automatizar esta tarea reduce errores humanos y permite aplicar buenas prácticas de administración.


    Se te pide desarrollar un script en Bash interactivo que permita:

    • Cambiar una interfaz de red entre DHCP y IP estática
    • Solicitar datos al usuario
    • Validar entradas básicas
    • Generar automáticamente el archivo de configuración Netplan
    • Aplicar los cambios con netplan apply
    • Crear copias de seguridad antes de modificar la configuración
    • Ofrecer un menú interactivo

    Requisitos técnicos

    El script debe:

    1. Ejecutarse únicamente como root
    2. Verificar que existe el archivo de Netplan
    3. Permitir elegir entre:
      • Configuración DHCP
      • Configuración IP estática
      • Salir
    4. En modo IP estática debe solicitar:
      • Dirección IP
      • Máscara en formato CIDR
      • Gateway
      • DNS
    5. Confirmar datos antes de aplicar cambios
    6. Crear copia de seguridad automática del archivo original
    7. Mostrar el archivo generado antes de aplicar
    8. Aplicar configuración con netplan apply
    9. Gestionar errores básicos

    Restricciones

    • El script debe estar escrito íntegramente en Bash
    • No se permite editar Netplan manualmente fuera del script
    • No se permite copiar directamente soluciones externas
    • Debe funcionar en Ubuntu Server con Netplan

    Análisis de solución

    Una vez terminado tu script puedes comparalo con el ejemplo de solución.

    Deberás:

    • Comparar tu solución con la propuesta
    • Detectar mejoras posibles
    • Identificar:
      • Validaciones adicionales
      • Buenas prácticas usadas
      • Manejo de errores
      • Estructura del código
      • Modularización en funciones

    El objetivo no es solo que funcione… sino entender cómo se escribe un script robusto de administración real.


    Ejemplo de solucion

    1. Idea general del script

    El script en Bash hará:

    1. Detectar qué archivo de Netplan estoy usando (normalmente algo como /etc/netplan/00-installer-config.yaml).
    2. Mostrar un menú en consola:
        1. Configurar IP estática
        1. Configurar IP dinámica (DHCP)
        1. Salir
    3. Según la opción:
      • Pedirá los datos necesarios (IP, máscara, gateway, DNS) si es estática.
      • Generará un archivo YAML de Netplan con esa configuración.
      • Ejecutará netplan apply para aplicar los cambios.

    IMPORTANTE:

    • Necesitaré ejecutarlo como root (sudo ./script.sh), porque va a escribir en /etc/netplan/.
    • Debo saber el nombre de la interfaz (por ejemplo enp0s3 o ens33). Lo puedo consultar mediante ip a o ifconfig

    2. Ejemplo de archivo Netplan (referencia rápida)

    Consulto mi archivo actual de Netplan mediante ls /etc/netplan primero y sudo cat /etc/netplan/50-cloud-init.yaml obteniendo las siguientes:

    DHCP (IP dinámica)

    network:
      version: 2
      ethernets:
        enp0s3:
          dhcp4: true

    «

    IP estática

    network:
      version: 2
      renderer: networkd
      ethernets:
        enp0s3:
          dhcp4: false
          addresses:
            - 192.168.1.50/24
          gateway4: 192.168.1.1
          nameservers:
            addresses:
              - 8.8.8.8
              - 1.1.1.1
    

    Nuestro script generará algo parecido según la opción que elijas.


    3. Script completo en Bash

    Mediante sudo nano script_cambiar_ip_netplan.sh,creo el script dentro de mi carpeta de scripts con ruta /home/mario/scripts. En su interior introduzco los siguientes parámetros de configuración:

    #!/usr/bin/env bash
    
    # ==========================================================
    # Script para cambiar entre IP estática y dinámica (DHCP)
    # en Ubuntu Server usando Netplan.
    #
    # Úsalo con:
    #   sudo ./cambiar_ip_netplan.sh
    # ==========================================================
    
    # ---------- CONFIGURACIÓN BÁSICA A EDITAR ----------
    # Nombre de la interfaz de red (ajústalo a tu caso: enp0s3, ens33, etc.)
    INTERFACE="enp0s3"
    
    # Archivo de Netplan a usar
    NETPLAN_FILE="/etc/netplan/50-cloud-init.yaml"
    # Si tu sistema usa otro, cámbialo aquí, por ejemplo:
    # NETPLAN_FILE="/etc/netplan/50-cloud-init.yaml"
    # ---------------------------------------------------
    
    # Comprueba que se ejecuta como root
    if [[ "$EUID" -ne 0 ]]; then
      echo "Este script debe ejecutarse como root. Usa: sudo $0"
      exit 1
    fi
    
    # Comprueba que existe el archivo de Netplan
    if [[ ! -f "$NETPLAN_FILE" ]]; then
      echo "ERROR: No se encuentra el archivo de Netplan: $NETPLAN_FILE"
      echo "Revísalo y edita el script para apuntar al archivo correcto."
      exit 1
    fi
    
    # Función para pausar
    pausa() {
      echo
      read -rp "Pulsa Enter para continuar..." _;
    }
    
    # Función para configurar DHCP
    configurar_dhcp() {
      echo "Configurando interfaz $INTERFACE con IP dinámica (DHCP)..."
    
      # Hacemos copia de seguridad
      cp "$NETPLAN_FILE" "${NETPLAN_FILE}.backup_$(date +%Y%m%d_%H%M%S)"
    
      cat > "$NETPLAN_FILE" <<EOF
    network:
      version: 2
      renderer: networkd
      ethernets:
        $INTERFACE:
          dhcp4: true
    EOF
    
      echo "Archivo Netplan generado:"
      echo "----------------------------------------"
      cat "$NETPLAN_FILE"
      echo "----------------------------------------"
    
      echo "Aplicando cambios con netplan apply..."
      netplan apply
    
      if [[ $? -eq 0 ]]; then
        echo "Configuración DHCP aplicada correctamente."
      else
        echo "Hubo un error al aplicar la configuración. Revisa el archivo YAML."
      fi
    
      pausa
    }
    
    # Función para configurar IP estática
    configurar_estatica() {
      echo "Configuración de IP estática para la interfaz: $INTERFACE"
      echo
    
      read -rp "Introduce la IP (ej: 192.168.1.50): " IP
      read -rp "Introduce la máscara en CIDR (ej: 24 para 255.255.255.0): " CIDR
      read -rp "Introduce la puerta de enlace (gateway) (ej: 192.168.1.1): " GATEWAY
      read -rp "Introduce DNS (separados por espacio, ej: 8.8.8.8 1.1.1.1): " DNS_ENTRIES
    
      # Validaciones básicas (muy simples)
      if [[ -z "$IP" || -z "$CIDR" || -z "$GATEWAY" || -z "$DNS_ENTRIES" ]]; then
        echo "Algún campo está vacío. Cancelando configuración."
        pausa
        return
      fi
    
      echo
      echo "Resumen de la configuración estática:"
      echo "  IP:           $IP/$CIDR"
      echo "  Gateway:      $GATEWAY"
      echo "  DNS:          $DNS_ENTRIES"
      echo
    
      read -rp "¿Es correcto? (s/n): " CONFIRM
      if [[ "$CONFIRM" != "s" && "$CONFIRM" != "S" ]]; then
        echo "Configuración cancelada."
        pausa
        return
      fi
    
      # Creamos una copia de seguridad del archivo actual
      cp "$NETPLAN_FILE" "${NETPLAN_FILE}.backup_$(date +%Y%m%d_%H%M%S)"
    
      # Generamos lista YAML de DNS
      DNS_YAML=""
      for dns in $DNS_ENTRIES; do
        DNS_YAML+="          - $dns"$'\n'
      done
    
      # Escribimos nueva configuración en el archivo Netplan
      cat > "$NETPLAN_FILE" <<EOF
    network:
      version: 2
      renderer: networkd
      ethernets:
        $INTERFACE:
          dhcp4: false
          addresses:
            - $IP/$CIDR
          gateway4: $GATEWAY
          nameservers:
            addresses:
    $(echo -n "$DNS_YAML")
    EOF
    
      echo "Archivo Netplan generado:"
      echo "----------------------------------------"
      cat "$NETPLAN_FILE"
      echo "----------------------------------------"
    
      echo "Aplicando cambios con netplan apply..."
      netplan apply
    
      if [[ $? -eq 0 ]]; then
        echo "Configuración estática aplicada correctamente."
      else
        echo "Hubo un error al aplicar la configuración. Revisa el archivo YAML."
      fi
    
      pausa
    }
    
    # Menú principal
    while true; do
      clear
      echo "=============================================="
      echo "   Cambiar configuración de red (Netplan)"
      echo "   Interfaz actual: $INTERFACE"
      echo "   Archivo Netplan: $NETPLAN_FILE"
      echo "=============================================="
      echo "1) Usar IP dinámica (DHCP)"
      echo "2) Usar IP estática"
      echo "3) Salir"
      echo "----------------------------------------------"
      read -rp "Elige una opción [1-3]: " OPCION
    
      case "$OPCION" in
        1)
          configurar_dhcp
          ;;
        2)
          configurar_estatica
          ;;
        3)
          echo "Saliendo..."
          exit 0
          ;;
        *)
          echo "Opción no válida."
          pausa
          ;;
      esac
    done
    

    4. Cómo usarlo paso a paso

    1. Crear el archivo del script: sudo nano script_cambiar_ip_netplan.sh Pega el contenido anterior, guarda y cierra (Ctrl+O, Enter, Ctrl+X).
    2. Dar permisos de ejecución: sudo chmod +x script_cambiar_ip_netplan.sh
    3. Editar variables según tu caso:
      • Abre el script y cambia: INTERFACE="enp0s3" NETPLAN_FILE="/etc/netplan/50-cloud-init.yaml" Pon el nombre de la interfaz que tengas y el archivo real de Netplan.
    4. Ejecutar el script como root: sudo ./script_cambiar_ip_netplan.sh
    5. Elegir opción del menú:
      • Si eliges estática, introduce:
        • IP (ej. 192.168.0.50)
        • CIDR (ej. 24)
        • Gateway (ej. 192.168.0.1)
        • DNS (ej. 8.8.8.8 1.1.1.1)

    5. Recomendaciones para no quedarte “sin red”

    • Haz pruebas estando en consola local (no por SSH), para que si te quedas sin red aún tengas acceso.
    • El script crea copias de seguridad del YAML original con nombre tipo:
      • /etc/netplan/00-installer-config.yaml.backup_20251206_153055
    • Si algo sale mal, puedes restaurar: sudo cp /etc/netplan/00-installer-config.yaml.backup_YYYYMMDD_HHMMSS /etc/netplan/00-installer-config.yaml sudo netplan applyM

    6. Evidencias de funcionamiento del Script en mi UbuntuServer.

    • A continuación adjunto distintas capturas de pantallas de mi UbuntuServer en las que puedo seleccionar las distintas opciones (yo no asignaré IP fija en este momento, ya que quiero seguir trabajando con mi server mediante DHCP)

    Arrancamos el script seleccionado la config por DHCP:

    Aquí vemos las opciones que nos ofrece, sin seleccionar ninguna:

    En esta simulamos introducir los parámetros para configurar IP estática:

    Y en esta última seleccionamos la opción de salir, y hacemos un ip a donde evidencia la asignación de IP por DHCP:

  • OWASP Top 10

    OWASP Top 10

    La seguridad en aplicaciones web se ha convertido en uno de los pilares fundamentales de la tecnología moderna. Desde los primeros años de Internet hasta las complejas arquitecturas distribuidas actuales, la superficie de ataque no ha dejado de crecer. Las organizaciones, instituciones educativas, empresas y desarrolladores individuales dependen a diario de servicios expuestos en la web: sistemas de comercio electrónico, portales de administración pública, aplicaciones financieras, APIs móviles, servicios en la nube y plataformas de datos.
    En este ecosistema hiperconectado, la seguridad ya no es opcional, y comprender cómo se diseñan, evalúan y protegen las aplicaciones es una competencia esencial.

    En este contexto nace OWASP, siglas de Open Web Application Security Project, un proyecto abierto creado en 2001 con un objetivo claro: mejorar la seguridad del software de manera global, accesible y colaborativa. OWASP no pertenece a ninguna empresa ni responde a intereses comerciales; es una comunidad independiente formada por miles de profesionales, investigadores, empresas, voluntarios y académicos que comparten herramientas, documentación, buenas prácticas y estándares destinados a reducir el riesgo en aplicaciones web.

    A lo largo de más de dos décadas, OWASP ha evolucionado de un pequeño repositorio comunitario a convertirse en la referencia internacional en materia de seguridad de aplicaciones. Sus proyectos son utilizados por desarrolladores, auditores, equipos de ciberseguridad, gobiernos y universidades. La calidad y madurez de sus aportaciones ha hecho que muchas de sus metodologías formen hoy parte de marcos oficiales, certificaciones internacionales e incluso procesos internos de desarrollo en grandes corporaciones.

    OWASP Top Ten 2021:

    El OWASP Top Ten: un estándar, no una lista

    Entre todos los proyectos que mantiene OWASP, el más conocido es el OWASP Top Ten, una clasificación periódica que identifica las diez categorías de riesgos más críticos en aplicaciones web.
    Es importante subrayar que no se trata de un listado arbitrario, sino de un estándar de seguridad utilizado para:

    • Establecer requisitos mínimos en auditorías.
    • Formar a desarrolladores y equipos técnicos.
    • Definir políticas de desarrollo seguro.
    • Cumplir normativas o marcos de compliance.
    • Priorizar esfuerzos de corrección en aplicaciones reales.

    El Top Ten se elabora a partir de datos aportados por empresas de seguridad, análisis de millones de aplicaciones, estadísticas de ataques y consensos de la comunidad. De esta forma se consigue una visión realista y basada en datos sobre qué riesgos son realmente explotados por atacantes y qué errores aparecen con mayor frecuencia en el software moderno.

    La edición vigente a día de hoy, OWASP Top 10 – 2021, supuso un cambio importante respecto a sus predecesores. No solo reorganizó las categorías tradicionales, sino que introdujo nuevos conceptos como Insecure Design y amplió la visión hacia la causa raíz de los fallos, no solo sus síntomas. Al mismo tiempo, OWASP continúa evolucionando y, en 2025, ya existe un Top Ten 2025 Release Candidate, en fase de revisión, que refuerza áreas como la cadena de suministro (software supply chain), la gestión de configuraciones y el manejo de errores críticos. Aunque aún no sustituye oficialmente a la versión de 2021, nos ofrece una visión clara de hacia dónde se dirige la seguridad web en los próximos años.

    La relevancia de OWASP en el panorama actual

    La importancia de OWASP no reside únicamente en su famoso Top Ten. Su comunidad mantiene numerosos proyectos fundamentales en ciberseguridad, entre ellos:

    • OWASP Testing Guide, para pruebas de seguridad sistemáticas.
    • OWASP ASVS, un estándar completo para verificar aplicaciones.
    • OWASP MASVS, equivalente para seguridad móvil.
    • OWASP Cheat Sheets, una colección esencial de guías rápidas prácticas.
    • OWASP ZAP, una herramienta gratuita utilizada mundialmente para pentesting web.

    Estos recursos permiten comprender, evaluar y corregir la seguridad desde múltiples perspectivas: desarrollo seguro, auditoría, análisis forense, arquitectura de software o gestión de riesgos.

    El valor de OWASP es especialmente relevante hoy porque:

    1. La mayoría de ataques exitosos explotan fallos básicos que OWASP describe y enseña a prevenir.
    2. El software moderno es más complejo que nunca: microservicios, contenedores, APIs, nubes híbridas, y automatización continua.
    3. Las brechas de seguridad tienen impactos empresariales enormes: pérdidas económicas, daños reputacionales, sanciones legales y robo masivo de datos.
    4. La industria necesita profesionales formados y actualizados, capaces de analizar código, revisar configuraciones, identificar vulnerabilidades y aplicar mitigaciones reales.

    Por ello, estudiar OWASP no significa memorizar diez riesgos, sino aprender a pensar como un desarrollador seguro, un auditor y un atacante ético simultáneamente. Es un enfoque integral que forma parte de la cultura DevSecOps moderna.

    Comprender OWASP es comprender cómo funciona realmente la seguridad en el desarrollo web en 2025. Y, sobre todo, significa adquirir la capacidad de construir aplicaciones más robustas, detectar fallos antes de que lo hagan los atacantes, y aplicar una mentalidad de seguridad continua que acompañe al software durante todo su ciclo de vida.

    Historia resumida de OWASP

    2001 – Fundación de OWASP

    Creada por Mark Curphey para compartir buenas prácticas de seguridad en aplicaciones web.

    2003 – Primer OWASP Top Ten

    Primera clasificación pública de los riesgos más graves de seguridad web.

    2007–2013 – Consolidación

    OWASP se convierte en referencia internacional. Se publican guías clave:

    • OWASP Testing Guide
    • OWASP Code Review Guide
    • ESAPI

    2017 – Edición crítica

    Se añaden nuevas categorías como XXE y Deserialización insegura.

    2021 – OWASP Top 10 moderno

    Reorganiza las vulnerabilidades por causa raíz, añade Insecure Design y Supply Chain.

    2025 – Release Candidate (no final todavía)

    Refina los riesgos modernos:

    • Aquí tienes las 10 categorías del OWASP Top Ten 2025 para aplicaciones web:
    • A01:2025 – Broken Access Control
      Fallos en el control de acceso que permiten a atacantes eludir restricciones y acceder a datos o funcionalidades no autorizadas. [owasp.org], [cybersecur…tynews.com]
    • A02:2025 – Security Misconfiguration
      Configuraciones inseguras por defecto, servicios expuestos o controles inconsistentes que amplían la superficie de ataque. [owasp.org], [cybersecur…tynews.com]
    • A03:2025 – Software Supply Chain Failures
      Vulnerabilidades en dependencias, sistemas de construcción, CD/CI y la infraestructura de distribución. [owasp.org], [cybersecur…tynews.com]
    • A04:2025 – Cryptographic Failures
      Fallos en encriptación por uso de algoritmos débiles, claves cortas o implementación incorrecta, exponiendo datos sensibles. [owasp.org], [cybersecur…tynews.com]
    • A05:2025 – Injection
      Inyección de código (SQL, OS, LDAP, etc.) por no sanitizar entradas del usuario, lo que permite ejecutar comandos maliciosos. [owasp.org], [cybersecur…tynews.com]
    • A06:2025 – Insecure Design
      Falta de diseño seguro desde etapas tempranas, abordando seguridad solo como parche, no de forma integral. [owasp.org], [cybersecur…tynews.com]
    • A07:2025 – Authentication Failures
      Deficiencias en mecanismos de autenticación como inicio de sesión o recuperación de cuentas, que facilitan suplantación. [owasp.org], [cybersecur…tynews.com]
    • A08:2025 – Software or Data Integrity Failures
      Fallos que permiten modificar el software o datos sin detección, comprometiendo su integridad. [owasp.org], [cybersecur…tynews.com]
    • A09:2025 – Security Logging & Alerting Failures
      Registro y alertas de seguridad insuficientes, que dificultan detectar ataques y responder adecuadamente. [owasp.org], [cybersecur…tynews.com]
    • A10:2025 – Mishandling of Exceptional Conditions
      Manejo deficiente de errores y condiciones excepcionales, que puede exponer datos sensibles o permitir DoS. [cybersecur…tynews.com], [cyberpress.org]

    Estas diez categorías representan los riesgos más críticos para la seguridad de las aplicaciones web según la edición 2025 del OWASP Top Ten, publicada oficialmente el 6 de noviembre de 2025.
    (Sigue vigente oficialmente 2021, pero 2025 ya marca la tendencia del futuro.)

    Tabla comparativa OWASP Top 10 (2021 vs 2025)

    Puesto 2021Categoría 2021Puesto 2025Categoría 2025Cambio principal
    1Broken Access Control1Broken Access ControlSe mantiene #1; SSRF (A10:2021) se consolida dentro de esta categoría. [owasp.org]
    2Cryptographic Failures4Cryptographic FailuresBaja de #2 a #4. [owasp.org]
    3Injection5InjectionBaja de #3 a #5. [owasp.org]
    4Insecure Design6Insecure DesignBaja de #4 a #6. [owasp.org]
    5Security Misconfiguration2Security MisconfigurationSube de #5 a #2 por mayor prevalencia de configuraciones complejas. [owasp.org]
    6Vulnerable and Outdated Components3Software Supply Chain FailuresExpandida al enfoque de cadena de suministro (dependencias, build, distribución). [owasp.org]
    7Identification and Authentication Failures7Authentication FailuresRenombrada para mayor precisión; posición similar. [owasp.org]
    8Software and Data Integrity Failures8Software or Data Integrity FailuresLigerísimo ajuste de nombre; categoría equivalente. [owasp.org]
    9Security Logging and Monitoring Failures9Security Logging & Alerting FailuresRenombrada enfatizando alertas accionables además del logging. [owasp.org]
    10Server-Side Request Forgery (SSRF)Eliminada como categoría independiente; integrada en A01: Broken Access Control. [owasp.org]
    10Mishandling of Exceptional ConditionsNueva en 2025: manejo inadecuado de errores/condiciones excepcionales (fallar “open”, fugas, DoS). [owasp.org], [cyberpress.org]

    Observaciones rápidas

    • Grandes movimientos: Security Misconfiguration sube con fuerza (#5 → #2); Cryptographic, Injection e Insecure Design descienden dos puestos. [owasp.org]
    • Evolución de componentes: Vulnerable and Outdated Components se amplía a Software Supply Chain Failures, reflejando el peso de ataques a la cadena de suministro (dependencias, CI/CD, repos, distribución). [owasp.org], [cybersecur…tynews.com]
    • Nuevas prioridades: Entra Mishandling of Exceptional Conditions (#10), centrada en errores y estados anómalos mal gestionados (excepciones, “fail-open”, fugas de información). [owasp.org], [cyberpress.org]
    • Consolidación: SSRF deja de ser categoría y se integra en Broken Access Control. [owasp.org]