Crea interfaces de línea de comandos profesionales con getopts de Bash
getopts
es un comando built-in de Bash que proporciona una forma robusta y estándar de procesar opciones de línea de comandos. Es la herramienta preferida para crear scripts que se comportan como comandos profesionales de Unix/Linux.
getopts
permite procesar opciones que siguen las convenciones estándar de Unix:
-h
, -v
, -f
-f archivo.txt
-hvf
Elemento | Descripción | Ejemplo |
---|---|---|
getopts optstring variable |
Sintaxis básica del comando | getopts "hvf:" opt |
optstring |
Cadena que define opciones válidas | "hvf:" (h,v,f con argumento) |
: después de letra |
Indica que la opción requiere argumento | f: requiere valor |
$OPTARG |
Variable que contiene el argumento | Valor de -f filename |
$OPTIND |
Índice del siguiente argumento | Para procesar argumentos restantes |
#!/bin/bash
# Script básico usando getopts
echo "Procesando opciones con getopts..."
# Variables por defecto
VERBOSE=false
HELP=false
FILENAME=""
# Procesar opciones
while getopts "hvf:" opt; do
case $opt in
h)
HELP=true
;;
v)
VERBOSE=true
;;
f)
FILENAME="$OPTARG"
;;
\?)
echo "Opción inválida: -$OPTARG" >&2
exit 1
;;
:)
echo "La opción -$OPTARG requiere un argumento." >&2
exit 1
;;
esac
done
# Mostrar ayuda si se solicita
if [ "$HELP" = true ]; then
echo "Uso: $0 [-h] [-v] [-f archivo]"
echo " -h: Mostrar ayuda"
echo " -v: Modo verbose"
echo " -f: Especificar archivo"
exit 0
fi
# Mostrar configuración si verbose está activo
if [ "$VERBOSE" = true ]; then
echo "=== CONFIGURACIÓN ==="
echo "Verbose: activado"
echo "Archivo: ${FILENAME:-'no especificado'}"
echo "===================="
fi
# Desplazar argumentos procesados
shift $((OPTIND-1))
# Mostrar argumentos restantes
if [ $# -gt 0 ]; then
echo "Argumentos adicionales: $*"
fi
echo "Script ejecutado correctamente"
./simple.sh -h
./simple.sh -v -f config.txt arg1 arg2
./simple.sh -vf datos.csv extra
#!/bin/bash
# Herramienta profesional de backup usando getopts
# Autor: Tu Nombre
# Versión: 1.0
# Variables globales
SCRIPT_NAME=$(basename "$0")
VERSION="1.0"
VERBOSE=false
DRY_RUN=false
COMPRESS=false
EXCLUDE_FILE=""
SOURCE_DIR=""
DEST_DIR=""
BACKUP_NAME=""
# Función para mostrar ayuda
mostrar_ayuda() {
cat << EOF
$SCRIPT_NAME v$VERSION - Herramienta Profesional de Backup
USO:
$SCRIPT_NAME [OPCIONES] ORIGEN DESTINO
OPCIONES:
-h Mostrar esta ayuda
-v Modo verbose (detallado)
-n Dry run (simular, no ejecutar)
-c Comprimir backup con gzip
-e ARCHIVO Archivo de exclusiones
-b NOMBRE Nombre personalizado del backup
-V Mostrar versión
ARGUMENTOS:
ORIGEN Directorio a respaldar
DESTINO Directorio donde guardar el backup
EJEMPLOS:
$SCRIPT_NAME /home/usuario /backup
$SCRIPT_NAME -v -c /documentos /backup
$SCRIPT_NAME -n -e excludes.txt /proyecto /backup
$SCRIPT_NAME -b "backup_importante" -v -c /datos /backup
ARCHIVO DE EXCLUSIONES:
El archivo de exclusiones debe contener un patrón por línea:
*.tmp
*.log
node_modules/
.git/
CÓDIGOS DE SALIDA:
0 Éxito
1 Error en argumentos
2 Error en validación
3 Error durante el backup
EOF
}
# Función para mostrar versión
mostrar_version() {
echo "$SCRIPT_NAME versión $VERSION"
}
# Función para logging
log() {
local nivel="$1"
shift
local mensaje="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
case "$nivel" in
"INFO")
[ "$VERBOSE" = true ] && echo "[$timestamp] INFO: $mensaje"
;;
"WARN")
echo "[$timestamp] WARN: $mensaje" >&2
;;
"ERROR")
echo "[$timestamp] ERROR: $mensaje" >&2
;;
"DEBUG")
[ "$VERBOSE" = true ] && echo "[$timestamp] DEBUG: $mensaje"
;;
esac
}
# Función para validar directorio
validar_directorio() {
local dir="$1"
local tipo="$2"
if [ ! -d "$dir" ]; then
log "ERROR" "El directorio $tipo '$dir' no existe"
return 1
fi
case "$tipo" in
"origen")
if [ ! -r "$dir" ]; then
log "ERROR" "No se puede leer el directorio origen '$dir'"
return 1
fi
;;
"destino")
if [ ! -w "$dir" ]; then
log "ERROR" "No se puede escribir en el directorio destino '$dir'"
return 1
fi
;;
esac
return 0
}
# Función para validar archivo de exclusiones
validar_exclusiones() {
local archivo="$1"
if [ ! -f "$archivo" ]; then
log "ERROR" "El archivo de exclusiones '$archivo' no existe"
return 1
fi
if [ ! -r "$archivo" ]; then
log "ERROR" "No se puede leer el archivo de exclusiones '$archivo'"
return 1
fi
return 0
}
# Función para realizar el backup
realizar_backup() {
local origen="$1"
local destino="$2"
local nombre="$3"
# Crear nombre del backup si no se especifica
if [ -z "$nombre" ]; then
nombre="backup_$(date +%Y%m%d_%H%M%S)"
fi
log "INFO" "Iniciando backup de '$origen' a '$destino/$nombre'"
# Preparar comando rsync
local rsync_cmd="rsync -av"
local rsync_options=""
# Agregar archivo de exclusiones si existe
if [ -n "$EXCLUDE_FILE" ]; then
rsync_options="$rsync_options --exclude-from=$EXCLUDE_FILE"
fi
# Construir comando completo
local comando="$rsync_cmd $rsync_options \"$origen/\" \"$destino/$nombre/\""
if [ "$DRY_RUN" = true ]; then
log "INFO" "DRY RUN - Se ejecutaría: $comando"
return 0
fi
# Ejecutar backup
log "DEBUG" "Ejecutando: $comando"
eval "$comando"
local resultado=$?
if [ $resultado -eq 0 ]; then
log "INFO" "Backup completado exitosamente"
# Comprimir si se solicita
if [ "$COMPRESS" = true ]; then
log "INFO" "Comprimiendo backup..."
if [ "$DRY_RUN" = true ]; then
log "INFO" "DRY RUN - Se comprimiría: $destino/$nombre.tar.gz"
else
tar -czf "$destino/$nombre.tar.gz" -C "$destino" "$nombre"
if [ $? -eq 0 ]; then
rm -rf "$destino/$nombre"
log "INFO" "Backup comprimido como: $destino/$nombre.tar.gz"
else
log "WARN" "Error al comprimir, manteniendo directorio sin comprimir"
fi
fi
fi
else
log "ERROR" "Error durante el backup (código: $resultado)"
return 3
fi
return 0
}
# Procesar opciones con getopts
while getopts "hvncVe:b:" opt; do
case $opt in
h)
mostrar_ayuda
exit 0
;;
v)
VERBOSE=true
log "DEBUG" "Modo verbose activado"
;;
n)
DRY_RUN=true
log "INFO" "Modo DRY RUN activado - no se realizarán cambios"
;;
c)
COMPRESS=true
log "DEBUG" "Compresión activada"
;;
V)
mostrar_version
exit 0
;;
e)
EXCLUDE_FILE="$OPTARG"
log "DEBUG" "Archivo de exclusiones: $EXCLUDE_FILE"
;;
b)
BACKUP_NAME="$OPTARG"
log "DEBUG" "Nombre de backup personalizado: $BACKUP_NAME"
;;
\?)
log "ERROR" "Opción inválida: -$OPTARG"
echo "Use '$SCRIPT_NAME -h' para ver la ayuda"
exit 1
;;
:)
log "ERROR" "La opción -$OPTARG requiere un argumento"
echo "Use '$SCRIPT_NAME -h' para ver la ayuda"
exit 1
;;
esac
done
# Desplazar opciones procesadas
shift $((OPTIND-1))
# Validar número de argumentos
if [ $# -ne 2 ]; then
log "ERROR" "Se requieren exactamente 2 argumentos: ORIGEN y DESTINO"
echo "Use '$SCRIPT_NAME -h' para ver la ayuda"
exit 1
fi
SOURCE_DIR="$1"
DEST_DIR="$2"
# Validaciones
log "INFO" "Validando parámetros..."
if ! validar_directorio "$SOURCE_DIR" "origen"; then
exit 2
fi
if ! validar_directorio "$DEST_DIR" "destino"; then
exit 2
fi
if [ -n "$EXCLUDE_FILE" ]; then
if ! validar_exclusiones "$EXCLUDE_FILE"; then
exit 2
fi
fi
# Mostrar configuración si verbose está activo
if [ "$VERBOSE" = true ]; then
echo ""
echo "========================================="
echo " CONFIGURACIÓN DE BACKUP "
echo "========================================="
echo "Origen: $SOURCE_DIR"
echo "Destino: $DEST_DIR"
echo "Nombre: ${BACKUP_NAME:-'automático'}"
echo "Verbose: $VERBOSE"
echo "Dry Run: $DRY_RUN"
echo "Comprimir: $COMPRESS"
echo "Exclusiones: ${EXCLUDE_FILE:-'ninguna'}"
echo "========================================="
echo ""
fi
# Realizar backup
realizar_backup "$SOURCE_DIR" "$DEST_DIR" "$BACKUP_NAME"
exit $?
#!/bin/bash
# Manejo avanzado de errores con getopts
# Configurar manejo silencioso de errores
OPTERR=0
# Variables
ARCHIVO=""
NUMERO=""
DIRECTORIO=""
QUIET=false
# Función de error personalizada
error_opcion() {
local opcion="$1"
local mensaje="$2"
if [ "$QUIET" = false ]; then
echo "Error: $mensaje" >&2
echo "Use '$0 -h' para ver la ayuda" >&2
fi
exit 1
}
# Función de ayuda
mostrar_ayuda() {
cat << EOF
Uso: $0 [OPCIONES]
OPCIONES:
-f ARCHIVO Especificar archivo (requerido)
-n NUMERO Especificar número (1-100)
-d DIR Especificar directorio
-q Modo silencioso
-h Mostrar ayuda
Ejemplo: $0 -f datos.txt -n 50 -d /tmp
EOF
}
# Validar número en rango
validar_numero() {
local num="$1"
# Verificar que sea un número
if ! [[ "$num" =~ ^[0-9]+$ ]]; then
return 1
fi
# Verificar rango
if [ "$num" -lt 1 ] || [ "$num" -gt 100 ]; then
return 1
fi
return 0
}
# Procesar opciones
while getopts ":f:n:d:qh" opt; do
case $opt in
f)
ARCHIVO="$OPTARG"
# Validar que el archivo no empiece con -
if [[ "$ARCHIVO" =~ ^- ]]; then
error_opcion "f" "El nombre de archivo no puede empezar con '-': $ARCHIVO"
fi
;;
n)
NUMERO="$OPTARG"
if ! validar_numero "$NUMERO"; then
error_opcion "n" "El número debe estar entre 1 y 100: $NUMERO"
fi
;;
d)
DIRECTORIO="$OPTARG"
if [[ "$DIRECTORIO" =~ ^- ]]; then
error_opcion "d" "El directorio no puede empezar con '-': $DIRECTORIO"
fi
;;
q)
QUIET=true
;;
h)
mostrar_ayuda
exit 0
;;
:)
case $OPTARG in
f) error_opcion "f" "La opción -f requiere un nombre de archivo" ;;
n) error_opcion "n" "La opción -n requiere un número" ;;
d) error_opcion "d" "La opción -d requiere un directorio" ;;
*) error_opcion "$OPTARG" "La opción -$OPTARG requiere un argumento" ;;
esac
;;
\?)
# Manejar casos especiales
case $OPTARG in
'') error_opcion "" "Se encontró una opción vacía" ;;
*) error_opcion "$OPTARG" "Opción desconocida: -$OPTARG" ;;
esac
;;
esac
done
# Validar opciones requeridas
if [ -z "$ARCHIVO" ]; then
error_opcion "f" "La opción -f (archivo) es requerida"
fi
# Validaciones adicionales
if [ -n "$ARCHIVO" ] && [ ! -f "$ARCHIVO" ]; then
error_opcion "f" "El archivo especificado no existe: $ARCHIVO"
fi
if [ -n "$DIRECTORIO" ] && [ ! -d "$DIRECTORIO" ]; then
error_opcion "d" "El directorio especificado no existe: $DIRECTORIO"
fi
# Mostrar configuración final
if [ "$QUIET" = false ]; then
echo "Configuración válida:"
echo " Archivo: $ARCHIVO"
echo " Número: ${NUMERO:-'no especificado'}"
echo " Directorio: ${DIRECTORIO:-'no especificado'}"
echo " Silencioso: $QUIET"
fi
echo "Script ejecutado correctamente"
#!/bin/bash
# Script que combina getopts con argumentos posicionales
# Ejemplo: ./script.sh -v -f config.txt proceso1 proceso2 proceso3
# Variables
VERBOSE=false
CONFIG_FILE=""
FORMATO="txt"
PROCESOS=()
# Procesar opciones
while getopts "vf:F:" opt; do
case $opt in
v)
VERBOSE=true
;;
f)
CONFIG_FILE="$OPTARG"
;;
F)
FORMATO="$OPTARG"
;;
\?)
echo "Opción inválida: -$OPTARG" >&2
exit 1
;;
:)
echo "La opción -$OPTARG requiere un argumento" >&2
exit 1
;;
esac
done
# Desplazar opciones procesadas
shift $((OPTIND-1))
# Los argumentos restantes son los procesos
PROCESOS=("$@")
# Validar que se proporcionaron procesos
if [ ${#PROCESOS[@]} -eq 0 ]; then
echo "Error: Debe especificar al menos un proceso"
echo "Uso: $0 [-v] [-f config] [-F formato] proceso1 [proceso2 ...]"
exit 1
fi
# Mostrar configuración si verbose está activo
if [ "$VERBOSE" = true ]; then
echo "=== CONFIGURACIÓN ==="
echo "Config file: ${CONFIG_FILE:-'ninguno'}"
echo "Formato: $FORMATO"
echo "Procesos: ${#PROCESOS[@]}"
for i in "${!PROCESOS[@]}"; do
echo " $((i+1)). ${PROCESOS[i]}"
done
echo "===================="
fi
# Procesar cada proceso
for proceso in "${PROCESOS[@]}"; do
echo "Procesando: $proceso"
# Aquí iría la lógica de procesamiento
[ "$VERBOSE" = true ] && echo " ✓ $proceso procesado"
done
echo "Todos los procesos han sido procesados"
Método | Ventajas | Desventajas | Uso Recomendado |
---|---|---|---|
getopts | Built-in, rápido, estándar | Solo opciones cortas | Scripts Unix/Linux estándar |
getopt (externo) | Opciones largas y cortas | Dependencia externa | Scripts complejos con muchas opciones |
Manual (case) | Control total, flexible | Más código, propenso a errores | Casos muy específicos |
$1, $2, $3... | Simple para pocos argumentos | No escalable, sin validación | Scripts muy simples |
Crea un script que use getopts para:
Desarrolla un script que: