Cuando ejecutas un programa, ese programa no existe en el vacío. El sistema le entrega un conjunto de pares clave=valor que contienen información sobre el contexto en que corre: quién lo está ejecutando, en qué idioma debe mostrar mensajes, dónde encontrar ejecutables. Eso es el entorno de un proceso, y cada par es una variable de entorno.
La distinción fundamental que hay que tener clara desde el principio: no toda variable de shell es una variable de entorno. Cuando escribes x=1 en la terminal, esa variable existe únicamente dentro de la shell actual. Si lanzas cualquier otro programa desde ahí, ese programa no sabrá que x existe. Para que un proceso hijo herede la variable, tienes que marcarla con export. Ese marcado le dice al sistema operativo “cuando hagas fork + exec para crear un proceso hijo, incluye esta variable en el bloque de entorno que le pasas”.
El mecanismo es parte del diseño UNIX: cada proceso tiene su propio espacio de entorno, que es una copia del entorno del padre en el momento del exec. Los cambios que el hijo haga en su propio entorno no afectan al padre, y los cambios que el padre haga después de lanzar al hijo tampoco llegan a él. El entorno se hereda una sola vez, al nacer.
¿Cuándo importa esto en la práctica? Cada vez que un script no encuentra un comando que tú sí encuentras, cada vez que una aplicación se comporta diferente lanzada desde cron que desde tu terminal, o cada vez que necesitas aislar un proceso para depurar si depende de alguna variable específica. Entender la herencia evita horas de depuración frustrante.
Lo que se rompe si lo ignoras: defines una variable en tu .bashrc, la usas en un script, y el script falla porque nunca usaste export. O modificas PATH en una sesión sin persistirlo, y la próxima terminal ya no encuentra tus herramientas personalizadas. O, peor, contaminas el entorno de un proceso de producción con variables de desarrollo.
La variable más crítica del entorno es PATH: una lista de directorios separados por : que la shell recorre de izquierda a derecha cada vez que escribes un comando. Cuando ejecutas git status, la shell busca un ejecutable llamado git en cada directorio de PATH en orden. El primero que encuentra lo usa. Si PATH está roto o incompleto, comandos que “deberían funcionar” no se encuentran.
# Ver el entorno completo del proceso de shell actual
env
# Ver solo las variables más relevantes para diagnóstico
echo "Usuario: $USER"
echo "Home: $HOME"
echo "Shell: $SHELL"
echo "Idioma: $LANG"
echo "Directorio actual: $PWD"
echo "Directorio anterior: $OLDPWD"
echo "PATH actual:"
# Reemplazar : por salto de línea para leer mejor
echo "$PATH" | tr ':' '\n'
# --- Demostración de herencia ---
# Variable de shell (solo vive en esta sesión, no se hereda)
proyecto="mi_app"
# Verificar: un subproceso no la ve
bash -c 'echo "proyecto desde hijo: ${proyecto:-[vacío]}"'
# Imprime: proyecto desde hijo: [vacío]
# Ahora exportamos la variable
export proyecto
# El hijo ya la hereda
bash -c 'echo "proyecto desde hijo: $proyecto"'
# Imprime: proyecto desde hijo: mi_app
# También se puede exportar y asignar en una sola línea
export entorno="produccion"
# Eliminar una variable del entorno y de la shell
unset entorno
echo "entorno ahora: ${entorno:-[eliminada]}"
# --- Modificar PATH de forma segura ---
# Añadir un directorio personal de scripts al PATH
# Siempre incluir $PATH existente para no romper lo que ya funciona
export PATH="$PATH:$HOME/.local/bin"
echo "PATH actualizado:"
echo "$PATH" | tr ':' '\n'
# Verificar que la shell ahora encontraría ejecutables en ese directorio
# (el directorio puede no existir aún; PATH lo acepta igual)
# --- Ejecutar un comando con entorno mínimo (diagnóstico) ---
# env -i arranca el comando sin heredar nada del entorno actual
# Útil para reproducir fallos de "funciona en mi máquina"
env -i HOME="$HOME" PATH="/usr/bin:/bin" bash --norc --noprofile -c '
echo "USER en entorno mínimo: ${USER:-[no definida]}"
echo "PATH en entorno mínimo: $PATH"
echo "which ls: $(which ls)"
'
# USER no estará definida porque no la pasamos a env -i
# which ls funcionará porque /bin está en el PATH mínimo que sí pasamos
Qué está pasando en cada parte
El primer env sin argumentos imprime todo el entorno del proceso actual tal como el sistema lo ve. No es la lista de variables definidas en tu .bashrc; es el estado vivo del entorno en ese momento. La diferencia importa cuando depuras: .bashrc se ejecuta al abrir una shell interactiva, pero un script lanzado de otra forma puede tener un entorno completamente distinto.
La demostración con bash -c es deliberada. Lanzar bash -c '...' crea un proceso hijo real, no una subshell del mismo proceso. Por eso es la prueba honesta de herencia: si el hijo no ve la variable, es porque el padre no la exportó. Fíjate en el uso de ${proyecto:-[vacío]} en lugar de $proyecto a secas: la sintaxis :-valor_por_defecto evita que bash se queje cuando la variable no existe, y hace el resultado del experimento más legible.
El unset entorno no solo borra el valor: elimina la variable completamente del entorno y de la shell. Si después haces echo $entorno, no obtienes cadena vacía (eso sería entorno=""), obtienes silencio absoluto o el valor por defecto si usas :-.
En la modificación de PATH, la parte crítica es "$PATH:/nuevo" y no "/nuevo" solo. Si escribieras export PATH="$HOME/.local/bin" sin incluir el $PATH anterior, perderías acceso a /usr/bin, /bin y todo lo demás: de repente ls, cp y grep dejan de encontrarse. Es el error clásico que deja la sesión casi inutilizable hasta que cierras y abres de nuevo.
Finalmente, env -i es la herramienta de diagnóstico que más se agradece tarde. Cuando un proceso falla en producción pero funciona en tu terminal, la diferencia casi siempre está en el entorno. Con env -i construyes el entorno mínimo necesario de forma explícita y puedes añadir variables una a una hasta reproducir el fallo, o hasta confirmar que el proceso funciona sin ninguna de ellas. El fragmento pasa HOME y un PATH mínimo porque bash los necesita para arrancar, pero omite deliberadamente USER para mostrar que en un entorno limpio muchas cosas que damos por sentadas simplemente no están.
Para que los cambios de PATH o cualquier otra variable persistan entre sesiones, hay que ponerlos en ~/.bashrc (para shells interactivas) o en ~/.profile (para shells de login). Lo que hagas directamente en la terminal desaparece al cerrarla.
N° 71