En esta página

Terminal y Bash: de cero a un script

Todo lo que sigue se hace escribiendo, no con el mouse. Si nunca abriste una terminal, empieza por aquí: al final vas a poder compilar, moverte por carpetas y escribir un script que compile el Rush 00 por tú.

Qué es una terminal (y qué es un shell)

Son dos cosas distintas que se usan juntas:

PiezaQué esEjemplo
Terminalla ventana que muestra textoGNOME Terminal, iTerm2, Windows Terminal
Shellel programa que interpreta lo que escribesbash, zsh, fish

La terminal es el vidrio; el shell es el que trabaja. Cuando ves algo así:

laura@maquina:~/42-piscina$

Eso es el prompt. Se lee: usuario laura, máquina maquina, carpeta actual ~/42-piscina, y $ significa "esperando una orden".

# en lugar de $ significa que eres root (administrador). Si ves # y no querías estar ahí, sal antes de escribir cualquier cosa.

Cómo se lee un comando

ls -la ex00
ParteNombreSignificado
lscomandoel programa que se ejecuta
-laopciones (flags)modifican su comportamiento
ex00argumentosobre qué actúa

Los flags cortos se pueden juntar: -l -a es lo mismo que -la. Los flags largos usan dos guiones: --all.

Casi todos los comandos traen su manual:

man ls          # manual completo (se sale con q)
ls --help       # resumen corto

Atajos que te salvan la vida

AtajoQué hace
Tabautocompleta nombres de archivos y comandos
Ctrl+Ccancela el comando que está corriendo
Ctrl+Llimpia la pantalla (como clear)
Ctrl+Rbusca en el historial de comandos
↑ / ↓recorre comandos anteriores
Ctrl+A / Ctrl+Eva al principio / final de la línea
Ctrl+Uborra desde el cursor hasta el principio

Tab es el atajo más importante: úsalo siempre y deja de escribir rutas a mano.

Rutas: dónde estás parado

Hay dos formas de nombrar una ubicación:

TipoEjemploSignifica
absoluta/home/laura/42-piscinadesde la raíz del sistema
relativaex00 o ../README.mddesde donde estás ahora

Símbolos que vas a ver todo el tiempo:

SímboloSignificado
/la raíz del sistema
~tu carpeta personal (/home/laura)
.la carpeta actual
..la carpeta de arriba
-la carpeta anterior (cd - vuelve)
pwd             # ¿dónde estoy?
cd ex00         # entro a ex00
cd ..           # vuelvo atrás
cd ~            # voy a mi carpeta personal
cd -            # vuelvo a la anterior

Ver archivos y moverse

ls              # lista
ls -l           # lista con detalles
ls -la          # incluye archivos ocultos (los que empiezan con .)
ls -lh          # tamaños legibles (K, M, G)
tree            # árbol de carpetas (puede no estar instalado)

Ver el contenido sin abrir un editor:

cat README.md        # todo de una
less README.md       # paginado: q para salir, / para buscar
head -20 README.md   # primeras 20 líneas
tail -20 README.md   # últimas 20
tail -f archivo.log  # sigue el archivo en vivo (Ctrl+C para cortar)
wc -l README.md      # cuenta líneas
file rush00          # qué tipo de archivo es

Crear, copiar, mover y borrar

ComandoQué haceCuidado
touch notas.txtcrea un archivo vacío—
mkdir ex01crea una carpeta—
mkdir -p a/b/ccrea toda la ruta—
cp origen destinocopia-r para carpetas
mv origen destinomueve o renombrapisa sin preguntar
rm archivoborrano hay papelera
rm -r carpetaborra carpeta y contenidono hay papelera

La regla del rm: en la terminal no existe "deshacer". Antes de escribir rm -rf algo, escribe pwd y ls algo. Dos segundos de verificación evitan perder el día. rm -rf / o rm -rf ~ son las dos formas más rápidas de destruir una máquina.

Buscar

find . -name "*.c"              # archivos .c desde aquí hacia abajo
find . -type d -name "ex*"      # solo carpetas
grep -rn "rush" ex00/           # busca el texto en todos los archivos
grep -n "int" ex00/main.c       # número de línea de cada coincidencia

grep es el que más vas a usar. -r recorre carpetas, -n muestra líneas, -i ignora mayúsculas.

Permisos: por qué un script "no se ejecuta"

ls -l rush00
-rwxr-xr-x  1 laura laura  16352 nov  1 13:00 rush00

Ese -rwxr-xr-x se lee en cuatro bloques:

-   rwx   r-x   r-x
│    │     │     │
│    │     │     └── otros: leer y ejecutar
│    │     └──────── grupo: leer y ejecutar
│    └────────────── dueño: leer, escribir y ejecutar
└─────────────────── tipo: - archivo, d carpeta, l enlace
LetraPermisoEn un archivoEn una carpeta
rreadleerlolistar su contenido
wwritemodificarlocrear/borrar dentro
xexecuteejecutarloentrar (cd)

Cambiar permisos:

chmod +x script.sh      # agrega ejecución para todos
chmod 644 archivo.txt   # rw-r--r--  (archivo normal)
chmod 755 script.sh     # rwxr-xr-x  (script ejecutable)
chmod 600 clave.txt     # rw-------  (solo el dueño)

Los números son la suma de r=4, w=2, x=1 por cada bloque (dueño, grupo, otros). 755 = 7 (4+2+1) + 5 (4+1) + 5 (4+1).

Si ./script.sh dice Permission denied, casi siempre es que falta chmod +x. No es que el script esté mal escrito.

Redirecciones y pipes: el superpoder

Todo programa tiene tres canales:

CanalNúmeroPara qué
entrada0 (stdin)de dónde lee
salida1 (stdout)a dónde escribe el resultado
errores2 (stderr)a dónde escribe los errores

Se pueden redirigir:

ls > lista.txt          # guarda la salida (pisa el archivo)
ls >> lista.txt         # agrega al final
wc -l < lista.txt       # lee la entrada desde un archivo
gcc main.c 2> errores.txt   # guarda solo los errores
gcc main.c > todo.txt 2>&1  # guarda salida y errores juntos
ls no-existe 2> /dev/null   # tira los errores a la basura

Y se pueden conectar programas con | (pipe): la salida de uno entra como entrada del siguiente.

ls -l | grep ".c"           # solo las líneas que contienen .c
cat README.md | wc -l       # cuenta líneas del README
history | grep gcc          # busca en tu historial
ls -l | sort -k5 -n | tail  # los 5 archivos más pesados

tee escribe en pantalla y en un archivo:

gcc main.c 2>&1 | tee compilacion.log

Procesos

ps aux              # todos los procesos
top                 # monitor en vivo (q para salir)
htop                # versión linda, si está instalada
kill 1234           # pide terminar el proceso 1234 (amable)
kill -9 1234        # lo mata sí o sí (último recurso)

Desde la terminal:

AtajoQué hace
Ctrl+Ctermina el proceso en primer plano
Ctrl+Zlo suspende y te devuelve el prompt
comando &lo lanza en segundo plano
jobslista los trabajos suspendidos
fgtrae el último al frente
bglo sigue en segundo plano

Si un programa "no termina", Ctrl+C es lo primero que hay que probar. Si no responde, abre otra terminal y busca el proceso con ps aux.

Variables de entorno

Una variable es una caja con nombre. En el shell se usa así:

NOMBRE="laura"         # asignar (sin espacios alrededor del =)
echo "$NOMBRE"          # leer
export NOMBRE           # hacerla visible a los programas que lances

Ese detalle del = es el error número uno:

NOMBRE = "laura"   # MAL: intenta ejecutar el comando "NOMBRE"
NOMBRE="laura"     # BIEN

La variable más importante es PATH: la lista de carpetas donde el shell busca los comandos.

echo "$PATH"            # carpetas separadas por :
which gcc               # ¿de dónde sale gcc?
export PATH="$PATH:$HOME/bin"   # agrega tu carpeta de scripts

Las comillas también importan:

echo $NOMBRE        # si está vacío, no imprime nada
echo "$NOMBRE"      # siempre una sola palabra, aunque tenga espacios

Regla práctica: comilla siempre las variables ("$VAR"). Evita el 90 % de los bugs de scripting.

Dónde guardar la configuración

ArchivoCuándo se carga
~/.bashrccada terminal interactiva de bash
~/.bash_profileal iniciar sesión (bash)
~/.zshrccada terminal interactiva de zsh
~/.config/fish/config.fishcada terminal de fish

Ahí van los export, los alias y el prompt:

alias ll="ls -la"
alias g="git"

Después de editar el archivo, recargalo:

source ~/.bashrc    # o ~/.zshrc

Scripts en Bash

Un script es un archivo de texto con comandos. La primera línea (el shebang) dice con qué programa ejecutarlo:

#!/usr/bin/env bash

/usr/bin/env bash busca bash en el PATH: es más portable que #!/bin/bash, que asume que bash está en esa ruta exacta.

Para ejecutarlo:

chmod +x script.sh   # una sola vez
./script.sh          # ejecutar

También se puede ejecutar sin permiso de ejecución:

bash script.sh       # no usa el shebang; lo corre con bash

El encabezado que evita desastres

#!/usr/bin/env bash
set -euo pipefail
FlagQué hace
-ecorta el script si un comando falla
-uerror si usas una variable no definida
-o pipefailun pipe falla si falla cualquiera de sus partes

Sin esto, un script sigue adelante después de un error y termina "bien" con resultados basura. Con esto, falla temprano y fuerte.

Argumentos

VariableSignificado
$0nombre del script
$1, $2…argumentos posicionales
$#cantidad de argumentos
$@todos los argumentos
$?código de salida del último comando (0 = bien)
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -ne 2 ]]; then
    echo "Uso: $0 ANCHO ALTO" >&2
    exit 1
fi

echo "Rectángulo de $1 x $2"

>&2 manda el mensaje al canal de errores: así el aviso no se mezcla con la salida normal del programa.

Condicionales

if [[ -f "ex00/main.c" ]]; then
    echo "existe"
elif [[ -d "ex00" ]]; then
    echo "existe la carpeta pero no el archivo"
else
    echo "no existe"
fi
PruebaVerdadera si…
[[ -f ruta ]]es un archivo
[[ -d ruta ]]es una carpeta
[[ -x ruta ]]es ejecutable
[[ -z "$var" ]]la cadena está vacía
[[ -n "$var" ]]la cadena no está vacía
[[ "$a" == "$b" ]]son iguales
[[ "$a" != "$b" ]]son distintas
[[ $n -eq 3 ]]números iguales (-ne, -lt, -gt, -le, -ge)

Dos corchetes [[ ]] son la versión moderna de [ ]: manejan mejor los espacios y las comillas. En scripts de bash, usa [[ ]].

También existen los atajos && (si salió bien) y || (si falló):

gcc main.c -o prog && ./prog        # compila y, solo si compiló, ejecuta
gcc main.c -o prog || echo "falló"  # avisa si falló

Bucles

for archivo in main.c ft_putchar.c rush00.c; do
    echo "Revisando $archivo"
done

contador=0
while [[ $contador -lt 3 ]]; do
    echo "Vuelta $contador"
    contador=$((contador + 1))
done

Recorrer líneas de un archivo (el patrón correcto):

while IFS= read -r linea; do
    echo "→ $linea"
done < lista.txt

IFS= evita que se coman los espacios; -r evita que se interpreten las barras invertidas. Sin las dos, los archivos con espacios se rompen.

Funciones

saludar() {
    local nombre="$1"
    echo "Hola, $nombre"
}

saludar "laura"

local hace que la variable viva solo dentro de la función. Es el equivalente a no contaminar el resto del script.

Script completo: compilar el Rush 00

Guarda esto como compilar.sh en la raíz del repo:

#!/usr/bin/env bash
set -euo pipefail

readonly CARPETA="ex00"
readonly ARCHIVOS=("main.c" "ft_putchar.c" "rush00.c")

for archivo in "${ARCHIVOS[@]}"; do
    if [[ ! -f "$CARPETA/$archivo" ]]; then
        echo "Falta $CARPETA/$archivo" >&2
        exit 1
    fi
done

gcc -Wall -Wextra -Werror \
    "$CARPETA/main.c" \
    "$CARPETA/ft_putchar.c" \
    "$CARPETA/rush00.c" \
    -o rush00

echo "Compilado: ./rush00"

Se ejecuta así:

chmod +x compilar.sh
./compilar.sh
./rush00

Cosas para mirar en el script:

LíneaPor qué está
set -euo pipefailsi algo falla, se entera
readonlyevita que se reasignen por accidente
ARCHIVOS=(...)un array: varios valores en una variable
"${ARCHIVOS[@]}"recorre el array respetando espacios
[[ ! -f ... ]]verifica antes de compilar
>&2 + exit 1error claro y código de salida correcto
\ al final de líneacontinúa el comando en la línea siguiente

Un script que termina con exit 0 implícito "dijo que salió bien". Si tu script detecta un problema, tiene que devolver un código distinto de cero. Así && y los sistemas de CI pueden reaccionar.

Revisar scripts con ShellCheck

shellcheck es un linter de bash: te marca comillas faltantes, variables sin usar, comparaciones mal escritas y decenas de trampas más.

shellcheck compilar.sh

Si no está instalado, buscalo en el gestor de paquetes de tu sistema. Es de las mejores inversiones de cinco minutos que existen.

Errores típicos de scripting

ErrorPor qué fallaSolución
VAR = 1los espacios convierten la asignación en un comandoVAR=1
$VAR sin comillasse rompe con espacios y globs"$VAR"
cd carpeta sin verificarsi falla, sigues operando en la carpeta equivocadacd carpeta || exit 1
for f in $(ls)se rompe con nombres con espaciosfor f in * o find
[ $x = y ] con x vacíoerror de sintaxis[[ "$x" == "y" ]]
#!/bin/bash en otro sistemabash puede estar en otra ruta#!/usr/bin/env bash
CRLF en el shebangel sistema busca bash\rdos2unix script.sh
rm -rf con variable vacíaborra desde la raízset -u y comillas
script sin chmod +xPermission deniedchmod +x

Chuleta

NecesitoComando
saber dónde estoypwd
listar con detallesls -la
cambiar de carpetacd ruta
crear carpetamkdir -p ruta
ver un archivoless archivo
buscar textogrep -rn "texto" .
buscar archivosfind . -name "*.c"
copiar carpetacp -r origen destino
borrar con cuidadorm -r carpeta
ver permisosls -l
hacer ejecutablechmod +x archivo
guardar salidacomando > archivo
encadenarcomando1 | comando2
ver procesosps aux
matar procesokill PID
buscar en historialCtrl+R
ver variableecho "$PATH"
editar configvim ~/.bashrc
recargar configsource ~/.bashrc

Siguiente parada: Git y GitHub Flow.


Hecho con amor para Laura, por Bogdan.