Teoría de Bash


Índice:

   Descriptores (fd -> file descriptor)

      Descriptores estándar en Unix

      Redirecciones típicas

   Expansiones Bash

      Asignación de valores

      Recorte de Strings

   Bloqueo - Impedir ejecución simultánea



Descriptores (fd -> file descriptor)  ⮴  

Un descriptor de archivo (fd, file descriptor) es simplemente un número entero que

el kernel usa como índice para referirse a un archivo abierto. No es el archivo en

sí, ni su ruta, ni su contenido — es solo un identificador numérico que representa

una conexión abierta entre tu proceso y ese archivo.


La mejor forma de verlo es como una tabla que el kernel mantiene por cada proceso:



Tabla de descriptores de archivo


Los tres primeros descriptores los tiene todo proceso desde que nace: 0 es stdin

(lo que lees del teclado), 1 es stdout (lo que se imprime en pantalla), y 2 es

stderr (los mensajes de error). Por eso >&2 en el script redirige al error están-

dar — estás escribiendo al fd número 2.


Descriptores estándar en Unix  ⮴  

| Número | Nombre   | Significado      |


|    0   |  stdin   | Entrada estándar |

|    1   |  stdout  | Salida estándar  |

|    2   |  stderr  | Salida de error  |


Redirecciones típicas  ⮴  


2>&1 significa: “manda el descriptor 2 al mismo sitio que el 1”

2>&1 equivale a  $> .


(1) En este caso se redirige stdout a out.txt y stderr a err.txt


Expansiones Bash  ⮴  

Asignación de valores  ⮴  

Opciones:

  1. ${var:-valor} → valor por defecto (sin modificar var)

  2. ${var:=valor} → asignar valor por defecto

  3. ${var:?error} → obligatorio o error

  4. ${var:+valor} → valor alternativo si existe


Expansión unset A   A=""   A="x"

${A:-v}     v        v       x

${A:=v}    A=v       A=v     x

${A:?e}    ❌      error    ❌ error x

${A:+v}    ``       ``       v


Recorte de Strings  ⮴  

Idea clave:


Opciones:

  1. ${var#patrón} → recorte corto desde el inicio

  2. ${var##patrón} → recorte largo desde el inicio

  3. ${var%patrón} → recorte corto desde el final

  4. ${var%%patrón} → recorte largo desde el final


Siendo s="foo/bar/baz.txt"


Expansión         Resultado

${s#*/}          bar/baz.txt

${s##*/}         baz.txt

${s%/*}          foo/bar

${s%%/*}         foo

${s%.*}          foo/bar/baz

${s%%.*}         foo/bar/baz


Ejemplo real en scripts


    log="/var/log/nginx/error.log"


  dir="${log%/*}"

  file="${log##*/}"

  name="${file%.*}"


  echo "$dir"

  echo "$file"

  echo "$name"



  Salida:

    /var/log/nginx

    error.log

    error


  Quitar doble extensión (.tar.gz)   

    name="${file%%.*}"




Bloqueo - Impedir ejecución simultánea  ⮴  

Para impedir la ejecución simultánea de un script se puede emplear el siguiente có-

digo:


    SCRIPT_REAL=$(realpath "$0")              # resuelve symlinks

    SCRIPT_NAME=$(basename "$SCRIPT_REAL")    # nombre del archivo real

    SCRIPT_NAME="${SCRIPT_NAME%.*}"           # sin extensión

    SCRIPT_NAME="${SCRIPT_NAME// /_}"         # sustituir espacios vacíos por _

    LOCKFILE="/var/lock/${SCRIPT_NAME}.lock"



      flock -n "$LOCKFD" || {

      echo "${SCRIPT_NAME} ya en ejecución, saliendo." >&2

      exit 1



      # tu código aquí


    ) {LOCKFD}>"$LOCKFILE"








usan patrones glob, no regex (* ? [...])


1️⃣ ${var#patrón} → recorte corto desde el inicio

path="/usr/local/bin/bash"

echo "${path#*/}"



Resultado:


usr/local/bin/bash



👉 Quita la coincidencia más corta del patrón desde el principio.


2️⃣ ${var##patrón} → recorte largo desde el inicio

echo "${path##*/}"



Resultado:


bash



👉 Quita la coincidencia más larga → muy usado para obtener el nombre de archivo.


📌 Equivale mentalmente a:


basename "$path"


3️⃣ ${var%patrón} → recorte corto desde el final

file="archivo.tar.gz"

echo "${file%.*}"



Resultado:


archivo.tar



👉 Quita la coincidencia más corta desde el final.


4️⃣ ${var%%patrón} → recorte largo desde el final

echo "${file%%.*}"



Resultado:


archivo



👉 Quita la coincidencia más larga desde el final.


📌 Equivale mentalmente a:


${file%%.*}  # nombre sin ninguna extensión


🧠 Regla visual ultra rápida

#  → izquierda

%  → derecha


uno   = corto

doble = largo


🧪 Tabla resumen

s="foo/bar/baz.txt"


Expansión Resultado

${s#*/} bar/baz.txt

${s##*/} baz.txt

${s%/*} foo/bar

${s%%/*} foo

${s%.*} foo/bar/baz

${s%%.*} foo/bar/baz

🧩 Patrones comunes (los que realmente usarás)

📁 Nombre de archivo

base="${path##*/}"


📂 Directorio padre

dir="${path%/*}"


📄 Quitar extensión

name="${file%.*}"


🧷 Quitar doble extensión (.tar.gz)

name="${file%%.*}"


⚠️ Importante: no es regex


Esto NO funciona:


${var#^foo}



Esto SÍ funciona:


${var#foo*}



Patrones válidos:


* cualquier cosa


? un carácter


[abc] clase de caracteres


💡 Ejemplo real en scripts

log="/var/log/nginx/error.log"


dir="${log%/*}"

file="${log##*/}"

name="${file%.*}"


echo "$dir"

echo "$file"

echo "$name"



Salida:


/var/log/nginx

error.log

error


🧠 Cuándo usarlos (regla práctica)


¿Manipulas strings simples? → ${#%}


¿Necesitas regex complejas? → sed / awk


¿Rendimiento y limpieza? → expansión Bash siempre





echo "${FOO:-bar}"

Si FOO no existe o está vacía → imprime bar

Si FOO existe → imprime su valor

NO asigna nada a FOO


Útil para: