Curso de Linux / Cómo ejecuta comandos Bash: ;, &&, ||, alias y variables de entorno
Bash lee los comandos, interpreta el nombre y los argumentos, aplica las expansiones y redirecciones necesarias, y luego los ejecuta. ; ejecuta el siguiente comando sin importar si el comando anterior tuvo éxito o no, && solo lo ejecuta si tuvo éxito, y || solo lo ejecuta si falló. Los alias cambian los comandos al momento de leerlos, y las variables de entorno son configuraciones que se pasan a los procesos hijo. Comprender esta diferencia permite explicar por qué el mismo texto puede producir resultados distintos.
Al principio, basta con familiarizarse con variables, variables de entorno, alias y el estado de salida, así como con el flujo básico de && y ||. El agrupamiento de comandos, las tuberías, la ejecución en segundo plano y los ejemplos completos de scripts se pueden leer según sea necesario. Cómo estructurar la ejecución en archivos se amplía en Primer script de Bash, y los límites del manejo de errores y la seguridad al trabajar con archivos se explican en Manejo de errores en Bash.
¿Cómo se determina el nombre del comando?
Los ejemplos de este curso están basados en Bash. El shell lee la línea de comandos, procesa argumentos, variables y redirecciones, y luego interpreta el nombre del comando. El comportamiento real puede variar dependiendo de si el nombre es un alias, una función, un comando interno del shell o un archivo ejecutable encontrado en PATH. Para verificar la interpretación actual del shell, ejecute lo siguiente.
type -a printf
type -a ls
command -v ls
La salida de type -a puede mostrar alias, funciones, comandos internos y candidatos a archivos ejecutables. command -v se puede usar para verificar si se puede encontrar un nombre de comando. PATH es una lista de directorios donde se buscan archivos ejecutables externos, pero no puede describir alias o funciones con una sola ruta de archivo. No sobrescriba arbitrariamente el PATH del sistema para practicar.
Ver el estado de salida y los símbolos de ejecución de un vistazo
Cuando un comando termina, el shell recibe un estado de salida (exit status). 0 significa éxito, un valor distinto de 0 indica fallo o un resultado diferente definido por el comando correspondiente. Por ejemplo, grep puede devolver 1 si no hay líneas coincidentes. $? es el estado de salida del comando ejecutado más recientemente, por lo que debe verificarse de inmediato o almacenarlo en una variable.
true
printf 'status=%s\n' "$?"
false
printf 'status=%s\n' "$?"
En orden, se imprimirán status=0, status=1. No es la frase que aparece en la pantalla como éxito lo que determina la ejecución condicional, sino el estado de salida. Si Bash no puede encontrar un comando, devuelve 127; si lo encuentra pero no puede ejecutarlo, devuelve 126; y si termina por una señal, se usa una regla de estado separada. No todos los fallos son 1.
| Símbolos | Rol |
|---|---|
; |
Ejecutar el siguiente comando independientemente del éxito o fallo |
&& |
Ejecutar el siguiente comando solo si tuvo éxito |
|| |
Ejecutar el siguiente comando si el estado no es 0 |
& |
Ejecutar en segundo plano sin esperar a que se complete |
| |
Conectar la salida estándar como entrada estándar del siguiente comando |
|& |
Pasar la salida estándar y el error estándar juntos a través de la tubería |
! |
Invertir lógicamente el estado de salida de un comando o tubería |
En la documentación de listas de comandos de Bash, ;, &, &&, || son operadores que componen listas de comandos. | y |& son operadores de tuberías y ! es una palabra reservada que invierte el estado de salida de la tubería. En este artículo, se usan true, false, printf, grep, cat, sleep en la terminal de Bash. Son comandos proporcionados por entornos comunes de Ubuntu y Rocky Linux y no requieren permisos de administrador.
Conectar comandos según el éxito o el fallo
Diferencia entre el punto y coma y la ejecución condicional
; ejecuta el siguiente comando después de que termina el comando anterior, independientemente del resultado. En una lista de comandos común, los saltos de línea también tienen la misma función de separación. Sin embargo, si la entrada no está completa debido a comillas, barras invertidas, tuberías o comandos compuestos, el salto de línea puede no terminar el comando.
false; printf 'always\n'
false && printf 'not run\n'
false || printf 'fallback\n'
true && printf 'next step\n'
always, fallback, next step se imprimen en orden, y not run no se imprime. && y || realizan una evaluación de cortocircuito (short-circuit evaluation) que omite el comando derecho si no se cumplen las condiciones de ejecución. Por ejemplo, cd /RUTA/REAL/DE/TRABAJO && COMANDO_DE_TRABAJO evita que se ejecute un trabajo en otra ubicación si el cambio de directorio falla. Esta notación es un patrón que se aplica sustituyendo con la ruta real y los comandos de trabajo preparados.
Orden de evaluación y distinción de bifurcaciones if
&& y || tienen la misma prioridad y se combinan de izquierda a derecha. Por lo tanto, incluso si A tiene éxito en A && B || C, si B falla, C se ejecuta.
true && false || printf 'fallback runs\n'
Se imprime fallback runs. Para elegir entre dos ramas únicamente según el resultado de la primera condición, se usa if como sigue.
if true; then
false
else
printf 'condition failed\n'
fi
El ejemplo anterior no avanza a else ni produce salida aunque false dentro de then falle. Sin embargo, la falla de la rama ejecutada no se recupera automáticamente. En un script de operación, se debe verificar el fallo de cada etapa y devolver el estado de terminación necesario.
Impresión de mensajes de error y preservación del estado de fallo
false || printf 'failure reported\n'
printf 'list status=%s\n' "$?"
Después de failure reported se imprime list status=0. || no es un símbolo que ignore errores de manera absoluta, pero el último printf ejecutado tuvo éxito, dejando el estado final de la lista en 0. Si se desea notificar el error y terminar en fallo, es necesario guardar el estado original y devolverlo mediante exit en el script.
Agrupar varios comandos en una sola tarea
Las llaves se ejecutan en el shell actual
{ ...; } ejecuta varios comandos en el shell actual. Se necesita un espacio entre las llaves y los comandos, y antes de la llave de cierre se requiere un punto y coma o un salto de línea.
LESSON_SCOPE=outer
{ LESSON_SCOPE=inner; printf '%s\n' "$LESSON_SCOPE"; }
printf '%s\n' "$LESSON_SCOPE"
unset LESSON_SCOPE
inner se imprime dos veces. Esto se debe a que las variables cambiadas dentro del grupo permanecen también en el shell actual. Las variables de práctica se eliminan con el último unset.
Los paréntesis se ejecutan en un sub-shell
LESSON_SCOPE=outer
( LESSON_SCOPE=inner; printf '%s\n' "$LESSON_SCOPE" )
printf '%s\n' "$LESSON_SCOPE"
unset LESSON_SCOPE
Se imprimen secuencialmente inner y outer. Los paréntesis crean un subproceso (subshell), que es un entorno de ejecución del shell separado, por lo que los cambios en las variables normales o en el directorio actual no permanecen en el shell exterior. No revierten efectos externos como la creación o eliminación de archivos o solicitudes a servicios externos. En el documento de agrupación de comandos de Bash se pueden verificar las diferencias entre ambos grupos.
Tubería, error estándar e inversión de estado de salida
Distinguir entre tubería y ejecución condicional
printf 'alpha\nbeta\n' | grep 'alpha'
Solo se imprime alpha. | conecta la salida estándar (standard output) del comando anterior con la entrada estándar (standard input) del siguiente comando. Su función es diferente de ||, que ejecuta el siguiente comando si el comando anterior falla. Los comandos dentro de una tubería no esperan al éxito del comando anterior para ejecutarse de manera selectiva, sino que procesan los datos que reciben.
También se transmite el error estándar mediante |&
bash -c 'printf "normal\n"; printf "error\n" >&2' |& cat
normal y error se envían todos a cat a través de la salida. El |& de Bash combina la salida estándar de error (standard error) con la salida estándar como 2>&1 | en este ejemplo. Si hay múltiples redirecciones existentes, el orden de aplicación también es importante, así que no sustituya sin revisar los símbolos. Cuando desee dejar la salida en pantalla y en un registro a la vez, puede usar tee con el siguiente comando, pero tenga en cuenta que la salida normal y los errores se combinan.
Invertir el estado de salida con !
! false
printf 'inverted status=%s\n' "$?"
Se envía la salida de inverted status=0. El false que originalmente estaba en un estado de fallo se convierte en éxito a efectos de la comprobación de la condición. Si el estado original es 0, ! se cambia a un estado distinto de 0. No se trata de una función que invierta mensajes de error o el contenido de un archivo.
Verificación de fallo en la tubería con pipefail
( set +o pipefail; false | cat; printf 'default=%s\n' "$?" )
( set -o pipefail; false | cat; printf 'pipefail=%s\n' "$?" )
Se imprimirán default=0 y pipefail=1. Por defecto, la tubería devuelve el estado del último comando, por lo que incluso si false falla, si cat tiene éxito, el estado global será 0. Al activar pipefail, solo será 0 si todos los comandos tienen éxito; si hay algún fallo, se usará el estado de salida del comando que falló más a la derecha. Cada comparación se ejecuta dentro de paréntesis, por lo que las opciones del shell existente no cambian. La documentación de Bash sobre pipelines explica esta regla.
Ejecución en segundo plano y verificación de la finalización de tareas
& ejecuta un comando de forma asincrónica sin esperar su finalización. Debe diferenciarse de &&, que solo procede al siguiente paso si tiene éxito. El siguiente ejemplo ejecuta un trabajo que espera poco tiempo y, cuando se desea, verifica el estado de finalización de ese trabajo con wait.
sleep 2 &
lesson_job=$!
printf 'foreground continues\n'
wait "$lesson_job"
printf 'job status=%s\n' "$?"
unset lesson_job
foreground continues se imprime primero, y cuando el trabajo termina, se imprime job status=0. $! es el ID del proceso del trabajo en segundo plano más reciente, y no se debe juzgar que el trabajo en segundo plano haya terminado exitosamente solo con $? inmediatamente después de la ejecución. En Bash interactivo, jobs, fg y bg se utilizan para consultar trabajos y cambiar entre primer plano y segundo plano. Simplemente adjuntar & no garantiza la ejecución después de cerrar sesión, así que para trabajos prolongados, elija un método apropiado de gestión de sesiones o de servicios.
Practicar el flujo de éxito o fracaso con todo el script
A continuación hay un ejemplo completo que solo imita el orden de ejecución de construcción, prueba y despliegue mediante mensajes. No se realiza una construcción real, despliegue ni eliminación de archivos. Guarde el contenido en un directorio de práctica vacío como un nuevo archivo llamado course15-flow.sh. Solo se necesita Bash para ejecutar, y no es necesario agregar permisos de ejecución.
#!/usr/bin/env bash
build() { printf 'build\n'; }
run_tests() { printf 'test\n'; return "${1:-0}"; }
deploy() { printf 'deploy\n'; }
build && run_tests "${1:-0}" && deploy
lesson_status=$?
if (( lesson_status != 0 )); then
printf 'workflow failed: status=%s\n' "$lesson_status" >&2
fi
exit "$lesson_status"
bash course15-flow.sh 0
echo $?
bash course15-flow.sh 1
echo $?
La primera ejecución imprime build·test·deploy y el estado es 0. Si cambia el argumento a 1, solo se ejecuta hasta build·test y se muestra un mensaje en el error estándar, y el estado es 1. Al ejecutar de nuevo como 0, vuelve al flujo de éxito. ${1:-0} es una expresión que escribe 0 si no hay un primer argumento o está vacío, y en esta práctica solo se deben usar 0 o 1 como argumento.
La función asigna un nombre a un conjunto de comandos con NOMBRE() { ...; } y devuelve un estado final con return. El ejemplo guarda el estado inmediatamente después de la ejecución en lesson_status, por lo que aunque se imprima un mensaje, el fallo original no desaparece. En un entorno real, en lugar de cada función, se deben usar tareas preparadas y verificadas, y redactar claramente el manejo de fallos paso a paso.
Para tareas independientes cortas, use punto y coma; para tareas que requieren el éxito de pasos anteriores, seleccione &&; para tareas que requieren múltiples ramas y recuperación, seleccione if·función. TAREA; LIMPIEZA se ejecuta aunque la tarea falle, pero no garantiza la ejecución de limpieza si el shell se cierra a la mitad o recibe señales. Dichas limpiezas deben diseñarse con trap, y no asuma que set -e maneja automáticamente todos los fallos. Como hay excepciones en las expresiones condicionales y en la lista &&·||, es importante acostumbrarse a verificar explícitamente el estado de salida.
Los alias se expanden cuando el shell los lee
alias crea nombres cortos para comandos frecuentes de Bash interactivo. Después de introducir la siguiente línea, en un nuevo prompt ejecute ll o type ll. Bash lee e interpreta la línea antes de ejecutarla: si agrupa la definición y el uso del alias en un mismo comando compuesto, puede que no se expanda como espera.
alias ll='ls -l'
type ll
ll
Los alias se aplican por defecto solo al shell actual y no se expanden en Bash no interactivo sin configuración adicional. No asuma que un script de automatización real depende de alias. Después de la práctica, puede eliminarlo con unalias ll. Para acciones que requieren recibir argumentos y reutilizar, es mejor considerar funciones de shell que alias.
Diferenciando variables de shell y variables de entorno
Las variables de shell están disponibles en el shell actual, y las variables export también se incluyen en el ambiente de los procesos hijos que se ejecutan posteriormente. El siguiente ejemplo utiliza nombres exclusivos para la práctica en lugar de variables de sistema comunes.
LESSON_MODE=practice
printf '%s\n' "$LESSON_MODE"
export LESSON_MODE
printenv LESSON_MODE
Ambas salidas son practice. La primera salida es el resultado de la expansión de la variable en el shell actual, y la segunda es el resultado de leer el ambiente que un proceso externo printenv recibió. export no reemplaza a otro shell que ya está en ejecución ni al proceso padre.
Para pasar un valor a un solo comando, se puede usar el formato NOMBRE=VALOR COMANDO. Al limpiar las variables de práctica, ejecute unset LESSON_MODE en el shell actual. No reutilice nombres de práctica para variables importantes como el directorio home del usuario o PATH del sistema.
Cómo verificar el orden de ejecución
- Si no sabe cómo se interpreta un comando, comience verificando
type -a NOMBRE. - Si la ejecución condicional no es como se espera, separe y verifique el estado de salida de cada etapa.
- Si un alias no funciona, verifique si está en un shell interactivo y si se ejecutó después de definirlo en una nueva línea de comandos.
- Si un comando hijo no puede leer la variable, revise la existencia de
export.
Los puntos y coma y el operador condicional, los alias y las variables intervienen todos en la 'forma de ejecutar comandos', pero no son la misma función. Antes de conectar comandos que realmente cambian archivos, primero verifique el flujo con comandos que tienen pocos efectos secundarios, como true, false y printf.
Documentación oficial
El documento de lista de comandos de GNU Bash explica las reglas de combinación de ;, && y ||, y el documento de alias de Bash explica el momento de la expansión. Las variables y el entorno se pueden consultar en el Manual de referencia de Bash.
Puede consultar las reglas detalladas sobre pipelines y pipefail, agrupación de comandos y estado de salida en la documentación oficial de GNU Bash.




