Fundamentos de los scripts de shell

ShellBeginner
Practicar Ahora

Introducción

Un script de shell almacena comandos en un archivo para que una tarea pueda repetirse de forma uniforme. Los scripts fiables para principiantes especifican claramente su intérprete, protegen las expansiones de variables con comillas, validan la entrada antes de actuar y devuelven un estado de salida que otros comandos pueden interpretar.

En este laboratorio, creará pequeños scripts Bash de forma progresiva. Ejecutará scripts mediante Bash y como archivos ejecutables, utilizará variables y argumentos posicionales, leerá entradas interactivas, tomará decisiones con if y pruebas de archivos, elegirá códigos de salida, iterará con for y combinará estos conceptos en un sencillo resumen de registros.

Cree y ejecute su primer script

En este paso, creará un script Bash, lo ejecutará mediante un intérprete y hará que pueda ejecutarse directamente.

Entre en el espacio de trabajo del laboratorio:

cd /home/labex/project/shell-lab

Cree hello.sh con un here-document. La primera línea es un shebang que selecciona Bash cuando el archivo se ejecuta directamente:

cat > hello.sh <<'EOF'
#!/bin/bash

echo "Hello from a Bash script"
pwd
EOF

Inspeccione el archivo antes de ejecutarlo:

cat hello.sh

Pase el archivo explícitamente a Bash. Esto funciona incluso antes de agregar el permiso de ejecución:

bash hello.sh

Agregue el permiso de ejecución e inspeccione el modo:

chmod u+x hello.sh
ls -l hello.sh

Ejecute el archivo desde el directorio actual. ./ proporciona una ruta; normalmente, el shell no busca automáticamente en el directorio actual:

./hello.sh

Use variables y comillas seguras

En este paso, asignará variables, las expandirá y comprobará por qué las comillas dobles protegen los valores que contienen espacios.

Cree un script con dos variables:

cat > variables.sh <<'EOF'
#!/bin/bash

course="Linux for Noobs"
workspace="/home/labex/project/shell-lab"

echo "Course: $course"
echo "Workspace: $workspace"
printf 'Quoted argument count: %s\n' "$#"
printf 'First argument: %s\n' "${1:-not provided}"
EOF

No coloque espacios alrededor de = en una asignación del shell. Las comillas dobles permiten expandir $course y, al mismo tiempo, conservar Linux for Noobs como un solo valor.

Ejecute el script con un argumento que contenga espacios:

bash variables.sh "beginner learner"

La variable especial $# contiene el número de argumentos posicionales y $1 contiene el primer argumento. ${1:-not provided} proporciona un valor alternativo cuando el argumento está ausente.

Guarde la salida para inspeccionarla:

bash variables.sh "beginner learner" > variable-output.txt
cat variable-output.txt

Valide los argumentos posicionales

En este paso, exigirá un argumento, mostrará indicaciones de uso y devolverá distintos estados de salida para los casos de error y éxito.

Cree greet-argument.sh:

cat > greet-argument.sh <<'EOF'
#!/bin/bash

if [ "$#" -ne 1 ]; then
  echo "Usage: $0 NAME" >&2
  exit 2
fi

name=$1
echo "Hello, $name"
exit 0
EOF

El comando de prueba [ ... ] comprueba si el número de argumentos no es igual a uno. >&2 envía el texto de uso a la salida de error estándar y exit 2 informa de un uso no válido.

Ejecútelo sin argumentos y, a continuación, muestre inmediatamente $?, que contiene el estado de salida del comando anterior:

bash greet-argument.sh
echo "missing argument status: $?"

El estado esperado es 2. Ejecútelo correctamente:

bash greet-argument.sh "Ada Lovelace"
echo "valid argument status: $?"

Guarde el saludo correcto:

bash greet-argument.sh "Ada Lovelace" > argument-greeting.txt

Lea una entrada interactiva

En este paso, mostrará una solicitud al usuario, leerá una línea de forma segura y gestionará una entrada vacía.

Cree ask-name.sh:

cat > ask-name.sh <<'EOF'
#!/bin/bash

read -r -p "Enter your name: " name

if [ -z "$name" ]; then
  echo "A name is required" >&2
  exit 1
fi

echo "Welcome, $name"
EOF

read -r lee una línea sin tratar las barras inversas de forma especial. -p muestra una solicitud y -z comprueba si el valor entre comillas está vacío.

Ejecute el script:

bash ask-name.sh

Cuando aparezca la solicitud, escriba:

Grace Hopper

Debería ver Welcome, Grace Hopper. Para guardar un resultado reproducible, canalice una línea de entrada al mismo script:

printf 'Grace Hopper\n' | bash ask-name.sh > interactive-greeting.txt
cat interactive-greeting.txt

Tome decisiones con pruebas de archivos

En este paso, utilizará if, elif y else junto con pruebas de archivos para clasificar una ruta.

Cree classify-path.sh:

cat > classify-path.sh <<'EOF'
#!/bin/bash

path=${1:-}

if [ -z "$path" ]; then
  echo "Usage: $0 PATH" >&2
  exit 2
elif [ -d "$path" ]; then
  echo "directory: $path"
elif [ -f "$path" ]; then
  echo "file: $path"
else
  echo "missing: $path" >&2
  exit 1
fi

exit 0
EOF

-d comprueba si se trata de un directorio y -f comprueba si se trata de un archivo normal. El orden es importante: el uso no válido se gestiona antes de determinar el tipo de ruta.

Pruebe los tres resultados posibles de la ruta:

bash classify-path.sh logs
bash classify-path.sh notes.txt
bash classify-path.sh missing.txt
echo "missing path status: $?"

Guarde juntos los resultados correctos. && ejecuta el segundo comando solo si el primero termina correctamente:

bash classify-path.sh logs > path-results.txt && bash classify-path.sh notes.txt >> path-results.txt
cat path-results.txt

Procese varios archivos con un bucle For

En este paso, iterará sobre archivos de registro y contará los registros coincidentes en cada uno.

Cree count-errors.sh:

cat > count-errors.sh <<'EOF'
#!/bin/bash

log_dir=${1:-logs}

if [ ! -d "$log_dir" ]; then
  echo "Log directory not found: $log_dir" >&2
  exit 1
fi

for log_file in "$log_dir"/*.log; do
  error_count=$(grep -c '^ERROR ' "$log_file" || true)
  printf '%s errors=%s\n' "$(basename "$log_file")" "$error_count"
done
EOF

En cada iteración, el bucle asigna una ruta coincidente a log_file. Las comillas de "$log_file" protegen los espacios, mientras que basename elimina el prefijo del directorio. grep -c cuenta las líneas coincidentes; || true evita que cero coincidencias detenga un flujo de trabajo estricto más grande.

Ejecute el bucle y guarde su salida:

bash count-errors.sh logs | tee error-counts.txt

Debería ver un resultado por cada archivo .log.

Combine validación, bucles y estados de salida

En este paso, creará un script reutilizable que valide un directorio, resuma sus registros e informe claramente si la operación tuvo éxito o falló.

Cree summarize-logs.sh:

cat > summarize-logs.sh <<'EOF'
#!/bin/bash

input_dir=${1:-}
output_file=${2:-}

if [ -z "$input_dir" ] || [ -z "$output_file" ]; then
  echo "Usage: $0 INPUT_DIR OUTPUT_FILE" >&2
  exit 2
fi

if [ ! -d "$input_dir" ]; then
  echo "Input directory not found: $input_dir" >&2
  exit 1
fi

: > "$output_file"

for log_file in "$input_dir"/*.log; do
  lines=$(wc -l < "$log_file")
  errors=$(grep -c '^ERROR ' "$log_file" || true)
  printf '%s lines=%s errors=%s\n' "$(basename "$log_file")" "$lines" "$errors" >> "$output_file"
done

echo "Wrote summary to $output_file"
exit 0
EOF

El comando : > "$output_file" crea el destino o lo vacía de forma segura antes del bucle. Ejecute el script con los dos argumentos obligatorios:

chmod u+x summarize-logs.sh
./summarize-logs.sh logs log-summary.txt
echo "summary status: $?"
cat log-summary.txt

Pruebe la ruta de error con un directorio inexistente:

./summarize-logs.sh missing log-summary.txt
echo "missing directory status: $?"

El estado de éxito debe ser 0, mientras que el estado para el directorio inexistente debe ser 1.

Resumen

Creó y ejecutó scripts Bash, utilizó shebangs y permisos, expandió variables entre comillas, leyó entradas posicionales e interactivas y devolvió estados de salida significativos. También tomó decisiones con if/elif/else, probó archivos y directorios, iteró sobre registros con for y combinó estos conceptos en un resumidor reutilizable.

Estos fundamentos hacen que la automatización posterior sea más fácil de leer, más segura de volver a ejecutar y más sencilla de evaluar para otros comandos o tareas programadas.