guía completa de scripting en Linux

Manual de Bash guía completa de scripting en Linux

Manual de Bash guía completa de scripting en Linux. Si tienes alguna inquietud recuerda contactarnos a través de nuestras redes sociales, o regístrate y déjanos un comentario en esta página para poder ayudarte. También puedes participar en el WhatsApp Ecuador.

Si trabajas con GNU/Linux tarde o temprano acabarás escribiendo tu propio script en Bash para automatizar tareas repetitivas. Copias de seguridad, limpieza de ficheros temporales, despliegues, pequeños tests… todo eso se hace mucho más llevadero cuando dominas un poco la Shell.

Esta guía quiere ser un auténtico “manual de Bash” en castellano con enfoque práctico, pensado para que puedas usarla como chuleta en el día a día. Verás desde qué es un script y cómo darle permisos, hasta variables, arrays, estructuras de control, funciones, redirecciones, tuberías, ejemplos reales y algunos comandos básicos de consola que complementan al lenguaje.

 

Qué es Bash y qué es un script Bash

Bash (Bourne Again SHell) es una shell de Unix y a la vez un lenguaje de scripting. Es la intérprete de comandos por defecto en la mayoría de distribuciones GNU/Linux (Debian, Ubuntu, etc.) y te permite tanto ejecutar órdenes interactivamente como guardarlas en ficheros para automatizarlas.

Un script Bash no es más que un archivo de texto plano con una serie de comandos que la shell ejecuta de forma secuencial, de la primera a la última línea, salvo cuando intervienen funciones, bucles o saltos de control.

Con unos cuantos comandos, condicionales e iteraciones, un simple fichero de texto puede convertirse en una pequeña herramienta administrativa potente: rotar logs, gestionar copias de seguridad, monitorizar servicios, limpiar directorios, lanzar tareas periódicas, etc.

 

Cómo crear y preparar un script Bash

Para que Linux trate un archivo como script Bash hay tres puntos básicos: shebang inicial, permisos de ejecución y nombre del fichero (la extensión es opcional, pero recomendable).

1. Indicar la shell a utilizar
La primera línea del script debe especificar qué intérprete lo ejecutará, mediante el llamado shebang:

#!/bin/bash

Si te interesa depurar lo que ocurre en cada paso, puedes activar el modo traza añadiendo -x al final de la línea:

#!/bin/bash -x

Con eso Bash mostrará en pantalla cada comando según se va ejecutando, lo que viene muy bien para detectar fallos en scripts grandes.

2. Conceder permisos de ejecución
Cuando terminas de editar el archivo, debes convertirlo en ejecutable. Los métodos más habituales son:

chmod 755 nombre_del_script
chmod +x nombre_del_script

Con cualquiera de las dos órdenes el sistema marca el fichero como ejecutable para su propietario (y, en el primer caso, también lectura/ejecución para grupo y resto).

3. Nombre y extensión del script
Linux no necesita extensión para saber con qué intérprete lanzar el archivo, porque esa información la toma del shebang de la primera línea. Aun así, es costumbre usar la extensión .sh para identificar rápidamente que se trata de un script de shell.

En Bash, todo lo que hay detrás del carácter # se considera comentario hasta el final de la línea (salvo en el shebang inicial). También puedes colocar comentarios después de un comando:

# Esta línea es un comentario completo
echo "Bienvenido" # Imprime un mensaje por pantalla

 

Tu primer script Bash paso a paso

Para editar scripts puedes usar cualquier editor de texto: gráficos como VS Code, Atom o Gedit, o clásicos de terminal como nano, vi o vim. Lo importante es que guarde texto plano sin formatos raros.

Vamos a crear un archivo llamado hola.sh con un típico “Hola Mundo” en Bash:

vim hola.sh

Dentro, escribe algo similar a esto (cada instrucción en su propia línea):

#!/bin/bash
echo "Hola. Soy tu primer script Bash"

El comando echo simplemente muestra por pantalla texto o valores de variables. Otra alternativa muy usada es printf, que permite formatear la salida (ancho de campos, alineación, etc.).

Guarda y cierra el editor (en vim, tecla ESC y luego :wq). Después, marca el fichero como ejecutable:

chmod 755 hola.sh

Para lanzarlo desde la terminal, sitúate en el directorio donde está el script y escribe:

./hola.sh

Deberías ver en la consola algo parecido a: Hola. Soy tu primer script Bash. A partir de aquí, la mecánica básica siempre será la misma: shebang, comandos, permisos y ejecución.

 

Parámetros posicionales y comando shift

Los scripts Bash pueden recibir argumentos desde la línea de comandos, que se exponen al interior del script mediante variables especiales llamadas parámetros posicionales.

Las variables de parámetros más usadas son:

  • $# – número total de argumentos recibidos.
  • $0 – nombre del script (con o sin ruta).
  • $1 ... $9 – los nueve primeros parámetros posicionales.
  • ${N} – acceso a un parámetro de índice superior a 9 (por ejemplo, ${10}).
  • $* – todos los argumentos como una sola cadena (excepto $0).
  • $@ – todos los argumentos como lista, útil para iterar correctamente.
  • $$ – PID (identificador de proceso) del script en ejecución.
  • $? – código de salida del último comando ejecutado.

El comando shift permite desplazar los parámetros posicionales hacia la izquierda. Por ejemplo, si $1 contiene UNO y $2 contiene DOS, tras ejecutar:

shift

el valor DOS pasará a $1 y el antiguo primer argumento se pierde. Esto es muy útil para procesar parámetros en bucle, sobre todo en scripts con muchos flags.

Un ejemplo típico en producción sería algo del estilo:

./find-truncate.sh --directory /var/tmp --max-size 300M --days 15

Donde el script analiza los parámetros recibidos (da igual el orden) e interpreta pares opción-valor como --directory /var/tmp, usando shift sucesivos para ir avanzando por la lista.

 

Procesado de parámetros con case y shift

Una forma cómoda de gestionar argumentos es combinar un bucle con case y shift para cada opción. Por ejemplo, un script que reciba un nombre y un apellido:

#!/bin/bash
# USO: ./nombre-apellido.sh --nombre NOMBRE --apellido APELLIDO
while ]
do
case "$1" in
-n|--nombre)
shift # saltamos a NOMBRE
nombre="$1"
shift # pasamos al siguiente parámetro
;;
-a|--apellido)
shift
apellido="$1"
shift
;;
*) # parámetro desconocido, lo ignoramos
shift
;;
esac
done
echo "Tu Nombre es: $nombre y tu Apellido es: $apellido"

Lanzando el script como:

./nombre-apellido.sh --nombre Luis --apellido Gutierrez

o bien cambiando el orden:

./nombre-apellido.sh --apellido Gutierrez --nombre Luis

se imprimirá la misma frase, porque el script no depende del orden de los argumentos, sino de su etiqueta.

 

Variables en Bash: tipos, ámbito y expansión

En Bash no existen tipos estrictos como en otros lenguajes; las variables son cadenas que pueden contener texto, números o arrays. No hay booleanos nativos, se trabaja con códigos de salida y comparaciones.

La declaración se realiza escribiendo el nombre seguido de un signo igual, sin espacios alrededor del =:

VAR=10 # número
VAR=texto # texto sin comillas
VAR='literal' # cadena sin expandir variables
VAR="cadena" # cadena donde SÍ se expanden variables

Si la cadena incluye espacios en blanco es imprescindible envolverla en comillas simples o dobles, o sólo se guardará la primera palabra. Por ejemplo:

NOMBRE=Luis
CALLE="Calle Larga"
Despacho=401

Otra forma muy potente de crear variables es capturando la salida de comandos. Hay dos sintaxis equivalentes:

USUARIO=$(whoami)
USUARIO=`whoami`

También es posible redirigir directamente stdin a una variable o leer ficheros enteros:

Texto=`cat fichero.txt`
read Texto < fichero.txt

Si quieres guardar en una variable tanto la salida estándar como los errores de un comando, redirige stderr a stdout dentro de la sustitución:

Salida=$(comando 2>&1)

Para concatenar variables y texto simplemente se escriben seguidas, opcionalmente usando comillas dobles para preservar espacios:

VarA=$OTRA
VarB="$V1 texto1 $V2 texto2"

Incluso puedes reutilizar el valor de la propia variable en la asignación:

Cadena="En un lugar"
Cadena="$Cadena de la Mancha" # Resultado: "En un lugar de la Mancha"

 

Comillas simples vs dobles y expansión de variables

La diferencia entre comillas simples y dobles es crítica en Bash. Lo que esté entre comillas simples se trata como texto literal: no se expanden variables ni secuencias especiales.

Por ejemplo:

VAR='NO procesar $V1'
echo "$VAR" # Imprime literalmente: NO procesar $V1

Con comillas dobles, en cambio, Bash sustituye variables y evalúa secuencias de escape:

VarA="En un lugar"
VarB='de la Mancha'
VarC="de cuyo nombre no quiero"
VarD=acordarme
TEXTO="$VarA $VarB $VarC $VarD"

Al mostrar $TEXTO se verá: En un lugar de la Mancha de cuyo nombre no quiero acordarme. Observa que VarB estaba con comillas simples, pero se expandió al insertarse fuera de ellas.

Si escribes algo como:

NONO='$3 $2 $1'

se guardará literalmente la cadena $3 $2 $1 y no los valores de esos parámetros. Esto es una fuente típica de confusiones cuando se empieza.

 

Arrays en Bash: creación, acceso y recorrido

Bash soporta arrays indexados numéricamente. Para declararlos puedes usar declare, typeset o la sintaxis entre paréntesis:

  • declare Marca
  • typeset Marca
  • Frutas=(Pera Manzana Platano)

En el primer caso se define un array vacío con capacidad para 9 posiciones (índices de 0 a 8). En el segundo, un array sin tamaño fijo, que irá creciendo según se le asignen elementos.

Para asignar un valor a una posición concreta se utiliza la notación ARRAY:

Marca="Tranqui-Cola"
COCHE="Seat"
COCHE="Opel"

Para leer elementos hay una sintaxis especial de expansión con llaves:

  • ${ARRAY} – elemento en la posición n.
  • ${ARRAY} – todos los elementos del array.
  • ${#ARRAY} – número de elementos definidos.
  • ${!ARRAY} – índices actualmente usados.

Por ejemplo:

Frutas=(Pera Manzana Platano)
echo ${Frutas} # Platano
echo ${Frutas} # Pera Manzana Platano
echo ${#Frutas} # 3
echo ${!Frutas} # 0 1 2

Si insertas un elemento en una posición salteada, puedes dejar huecos:

Frutas=(Pera Manzana Platano)
echo "Num Elementos: ${#Frutas}" # 3
echo "Indices en uso: ${!Frutas}" # 0 1 2
Frutas="melocoton"
echo "Num Elementos: ${#Frutas}" # 4
echo "Indices en uso: ${!Frutas}" # 0 1 2 5
echo ${Frutas} # Pera Manzana Platano melocoton

Para recorrer todos los elementos de un array, sobre todo cuando puede haber huecos, lo más robusto es iterar sobre la lista de índices válidos:

for i in ${!ARRAY}
do
echo "ARRAY = ${ARRAY}"
done

También puedes concatenar arrays fácilmente:

Unix=('SCO' 'HP-UX' 'IRIX' 'XENIX')
Linux=('Debian' 'Suse' 'RedHat')
NIX=("${Unix}" "${Linux}")
echo ${NIX} # SCO HP-UX IRIX XENIX Debian Suse RedHat

Estructuras de control: if, bucles, case y select

Bash incluye las estructuras típicas de cualquier lenguaje imperativo: condicionales, bucles y selección múltiple. La forma general del if es:

if
then
comandos
fi

Si quieres una rama alternativa:

if
then
comandos_si
else
comandos_no
fi

Para múltiples casos:

if
then
comandos1
elif
then
comandos2
else
comandos_por_defecto
fi

Los bucles for admiten dos sabores: estilo lista y estilo C:

for var in lista
do
comandos_usando "$var"
done

for ((i=0; i<5; i++))
do
comandos_usando "$i"
done

Los bucles while y until repiten mientras se cumpla o no se cumpla una condición:

while condicion
do
comandos
done

until condicion
do
comandos
done

La estructura case es ideal para discriminar según un valor concreto:

case $VARIABLE in
valor1)
comandos_opcion_1
;;
valor2|valor3|valor4)
comandos_para_varios_casos
;;
valorN)
comandos_opcion_N
;;
*)
comandos_por_defecto
;;
esac

Por último, select genera menús sencillos en la terminal, muy útil para scripts interactivos:

#!/bin/bash
PS3="Escoja una opcion: "
select Opcion in "Actualizar" "Listar" "Salir"
do
echo "Has elegido: $Opcion"
if
then
break
fi
done

Para abandonar bucles se usan break (romper el bucle) y continue (saltar al siguiente ciclobreak N para salir de varios bucles anidados de golpe.

 

Operadores lógicos, aritméticos y de texto

Al trabajar con if es fundamental respetar los espacios: la condición estándar usa corchetes con espacios alrededor:

if ||
then
...
fi

Los operadores aritméticos habituales son:

  • + suma
  • - resta
  • * multiplicación
  • / división entera
  • % módulo
  • ** potencia
  • ++ incremento
  • -- decremento

Para calcular expresiones numéricas se usan dos sintaxis equivalentes:

echo $((2+5)) # 7
echo $ # 3
i=1; echo $ # 2
echo $ # 9

Con cadenas de texto puedes hacer desde obtener la longitud hasta reemplazos masivos:

  • ${#cadena} – longitud de la cadena.
  • ${cadena:N} – subcadena desde la posición N.
  • ${cadena:N:M} – subcadena de M caracteres desde N.
  • ${cadena#texto} – elimina prefijo si coincide.
  • ${cadena%texto} – elimina sufijo si coincide.
  • ${cadena/texto1/texto2} – reemplaza primera coincidencia.
  • ${cadena//texto1/texto2} – reemplaza todas.
  • ${cadena/#texto1/texto2} – reemplazo sólo si está al inicio.
  • ${cadena/%texto1/texto2} – reemplazo sólo si está al final.
  • array=(${cadena/delimitador/ }) – transformar una cadena en array, cambiando delimitador por espacios.

Para comparar texto:

  • == igual
  • != distinto
  • > mayor (alfanumérico)
  • < menor (alfanumérico)
  • -z cadena vacía
  • -n longitud no cero

Para números se utilizan operadores específicos dentro de :

  • -eq igual
  • -gt mayor que
  • -ge mayor o igual
  • -lt menor que
  • -le menor o igual
  • -ne distinto

Y para archivos y directorios hay un completo conjunto de test:

  • -e existe (archivo o directorio).
  • -s existe y no está vacío.
  • -d es un directorio.
  • -f es un fichero regular.
  • -r tiene permiso de lectura.
  • -w tiene permiso de escritura.
  • -x es ejecutable (o se puede acceder, si es dir).
  • -O el usuario actual es propietario.
  • -G pertenece al grupo del usuario activo.
  • -nt más reciente que otro fichero.
  • -ot más antiguo que otro fichero.

Entrada, salida, redirecciones y tuberías

En Bash todo se basa en tres flujos básicos: stdin, stdout y stderr:

  • stdin (descriptor 0) – entrada estándar, normalmente el teclado.
  • stdout (descriptor 1) – salida estándar, suele ser la pantalla.
  • stderr (descriptor 2) – salida de error, también la pantalla por defecto.

Estos flujos se pueden redirigir hacia archivos o dispositivos especiales:

  • /dev/null – “agujero negro”, todo lo que le envíes se descarta.
  • /dev/random – genera números pseudoaleatorios.
  • /dev/urandom – números aleatorios de alta calidad.
  • /dev/zero – produce bytes nulos (0x00).
  • /dev/full – siempre se comporta como disco lleno al escribir.

Las redirecciones más habituales son:

  • > Fich – envía stdout (y a menudo stderr) a fichero, sobreescribiendo.
  • >> Fich – añade al final de un fichero existente.
  • 1> Fich – sólo stdout al fichero.
  • 2> Fich – sólo errores al fichero.
  • 1> F1 2> F2 – salidas normal y de error separadas.
  • > Fich 2>&1 – envía stdout al fichero y redirige stderr hacia donde vaya stdout.
  • < Fich – lee el contenido de un fichero como entrada del comando.

Por ejemplo, para listar una carpeta y guardar el resultado:

ls -1 /tmp/Carpeta1 > /tmp/lista_ficheros.txt

Si quieres separar errores de resultados:

ls -1 /tmp/Carpeta1 1>/tmp/lista_ficheros.txt 2>/tmp/errores.txt

O ignorar por completo los errores:

ls -1 /tmp/Carpeta1 1>/tmp/lista_ficheros.txt 2>/dev/null

Y si deseas que no aparezca nada (ni siquiera errores) en pantalla:

ls -l /tmp/Carpeta1 >/dev/null 2>&1

Las tuberías (|) permiten conectar la salida de un comando con la entrada de otro, construyendo verdaderos “pipelines” de datos:

ls -1 | sort # lista ordenada alfabéticamente
ls -1 | sort -r # misma lista en orden inverso

También se pueden encadenar varias:

RESOLUCION=$(xrandr | grep current | awk -F "," '{print $2}' | awk '{print $2"x"$4}')
echo $RESOLUCION

 

Funciones en Bash y ámbito de las variables

Las funciones permiten reutilizar bloques de código dentro de un mismo script, mejorando legibilidad y mantenimiento. Pueden definirse de dos maneras equivalentes:

function NombreFuncion {
comandos
}

NombreFuncion () {
comandos
}

Las llaves { } delimitan el cuerpo de la función. Importante: definir una función no la ejecuta; hay que llamarla explícitamente por su nombre en algún punto del script.

En cuanto al ámbito de las variables, Bash distingue entre:

  • Globales: declaradas fuera de funciones, accesibles desde cualquier parte del script.
  • Locales: creadas dentro de una función con la palabra clave local, sólo visibles dentro de ella.

Ejemplo ilustrativo:

#!/bin/bash
function AlgoPasa {
local UNO="Texto uno"
DOS="Texto dos" # sin local => global
echo "DENTRO FUNCION -> UNO=$UNO, DOS=$DOS"
}
UNO="1" # global
DOS="2" # global
echo "ANTES FUNCION -> UNO=$UNO, DOS=$DOS"
AlgoPasa
echo "DESPUES FUNCION -> UNO=$UNO, DOS=$DOS"

Verás que la variable local UNO no altera el valor global UNO, mientras que DOS sí resulta modificada tras llamar a la función, al no haberse declarado como local dentro de ella.

 

Códigos de salida, exit y return

Todo programa que termina en GNU/Linux devuelve un código de salida: 0 si todo fue bien y un valor distinto de cero en caso de error. En Bash ese código se encuentra en la variable especial $? inmediatamente después de cada comando.

En un script es buena práctica terminar con un exit N que exprese el resultado global de la ejecución. Por ejemplo:

#!/bin/bash
RUTA="$1"
ls -l "$RUTA" 2>/dev/null
CodError=$?
if ; then
echo "Todo correcto"
else
echo "Se produjo algún error"
fi
exit $CodError

Las funciones, por su parte, emplean la orden return para indicar un código numérico de salida. Si tienes que devolver cadenas, arrays u otras estructuras, se suele utilizar una variable global para guardar el resultado.

Ejemplo de función que une dos palabras y script que controla errores:

#!/bin/bash
function Une {
local palabra1=$1
local palabra2=$2
Cadena="$palabra1 $palabra2" # variable global
}
P1="$1"
P2="$2"
if ||
then
echo "Escriba 2 palabras por favor"
exit 1
else
Une "$P1" "$P2"
echo "$Cadena"
exit 0
fi

Ejemplos prácticos de scripts Bash reales

Para leer un fichero línea a línea, controlando que exista primero, podrías usar algo así:

#!/bin/bash
FICHERO="$1"
if
then
while read LINEA
do
echo "$LINEA"
done < "$FICHERO"
exit 0
else
echo "Debe indicar un fichero válido"
exit 1
fi

Un ejemplo más elaborado sería un script para grabar el escritorio usando ffmpeg y diálogos gráficos con zenity, que calcula la resolución actual mediante un pipeline de comandos como xrandr, grep y awk, lanza la captura y ofrece otra llamada posterior al mismo script para detenerla y mostrar la ruta del vídeo generado.

Otro caso interesante son scripts para configurar sudo de forma cómoda o segura. Por ejemplo, uno que añada al usuario corriente a /etc/sudoers.d exigiendo contraseña cada vez:

#!/bin/bash
echo "Debe proporcionar la clave de root cuando se le solicite"
echo "$USER ALL=(ALL:ALL) ALL" > /tmp/autorizado_$USER
su -c "cp /tmp/autorizado* /etc/sudoers.d/."

O el mismo concepto pero configurando sudo sin pedir contraseña:

#!/bin/bash
echo "Debe proporcionar la clave de root cuando se le solicite"
echo "$USER ALL = (ALL) NOPASSWD: ALL" > /tmp/autorizado_$USER
su -c "cp /tmp/autorizado* /etc/sudoers.d/."

Hay incluso scripts lúdicos que aprovechan tput, printf y arrays asociativos para dibujar “nieve” cayendo en la consola moviendo el cursor por la pantalla y dejando caer asteriscos columna a columna, jugando con la variable de entorno $RANDOM para la posición de cada copo.

 

Comandos básicos de consola que complementan Bash

Para moverte con soltura en la shell es fundamental conocer los comandos básicos del sistema y su documentación, además de trucos de Linux para exprimir la terminal. El propio Bash se apoya constantemente en estas utilidades externas.

El comando man muestra el manual de cualquier orden o función de C estándar:

man ls
man fopen

Para orientarte por el sistema de archivos:

  • pwd – muestra la ruta absoluta del directorio actual.
  • cd – cambia de directorio; acepta rutas absolutas o relativas.
  • ls – lista el contenido de un directorio, con opciones como -l para formato largo o -a para incluir archivos ocultos.
  • tree – imprime el árbol de directorios y archivos desde una ruta dada.

Para crear, copiar, mover o borrar archivos:

  • mkdir – crea un directorio vacío.
  • touch – crea un archivo o actualiza su fecha de modificación.
  • cp – copia archivos o directorios.
  • mv – mueve o renombra un archivo o carpeta.
  • rm – elimina archivos (con cuidado, y rm -r para directorios).

Para visualizar contenido:

  • less – paginador para ver ficheros de forma interactiva.
  • cat – concatena y muestra archivos completos.
  • head – muestra las primeras líneas (por defecto 10, o las que indiques con -n).
  • tail – muestra las últimas líneas; con -f sigue en vivo (log tailing).
  • hexdump – imprime datos binarios en formato hexadecimal.
  • grep – filtra líneas que coinciden con un patrón.

En cuanto a permisos y ownership:

  • chmod – cambia permisos (modo lectura, escritura, ejecución).
  • chown – cambia el usuario propietario de un archivo.

Hay utilidades especialmente útiles al programar o depurar:

  • htop – visor interactivo de procesos, muy práctico para ver uso de CPU y RAM, filtrar por nombre y enviar señales.
  • ifconfig – muestra interfaces de red y direcciones IP.
  • nano – editor de texto ligero en consola, fácil para editar configuraciones.
  • lsof – lista archivos abiertos; con -i ayuda a ver qué proceso escucha en un puerto.
  • loadkeys – cambia el mapa de teclado de la sesión actual.

Finalmente, las variables de entorno permiten configurar comportamiento de la shell y de muchos programas. Puedes verlas con:

env

y mostrar una concreta con:

echo $USER

Para crear o modificar temporalmente una variable en la sesión actual:

export MI_VARIABLE='aguante sistemas operativos'

Si quieres que persista en nuevas terminales, añade esa línea al final de ~/.bashrc con un editor como nano y vuelve a abrir la consola para que se cargue.

Con todas estas piezas -scripts, variables, arrays, control de flujo, redirecciones, funciones, ejemplos prácticos y utilidades de consola- ya tienes una base sólida para moverte con soltura en Bash y convertir la terminal en una auténtica navaja suiza para tu trabajo diario, tanto si administras sistemas como si estás aprendiendo programación desde cero.