Variables de entorno en Linux: verlas, cambiarlas y entender export

Si un programa no encuentra una herramienta, utiliza un idioma inesperado o sólo funciona desde una terminal concreta, puede que la diferencia esté en las variables de entorno. No son una lista única para todo Linux. Cada proceso recibe unos valores y puede transmitirlos a los programas que inicia.

Desde la terminal podemos consultar esos valores, preparar uno para una sola orden o exportarlo para los procesos que abramos desde esa sesión. La parte importante es distinguir una variable de la shell de una que realmente llega al programa.

Vamos a comprobarlo con nombres de prueba, sin modificar PATH, HOME ni archivos de configuración del usuario para seguir los ejemplos.

Consultar una variable sin mostrar todo el entorno

Para consultar el idioma que recibe un programa podemos ejecutar:

printenv LANG

El nombre se pasa sin el signo $. printenv busca esa variable dentro del entorno que ha recibido y muestra su valor si existe. Para consultar varias:

printenv LANG LC_ALL

Es posible que una esté definida y otra no. No debemos interpretar una salida vacía como una avería automática: depende de qué variable consultamos y de si existe o tiene un valor vacío.

La documentación de printenv explica su funcionamiento y sus códigos de salida. Si no encuentra una de las variables solicitadas, devuelve un estado distinto de cero.

También podemos ejecutar printenv sin argumentos para listar el entorno completo. Antes de copiar esa salida en un foro, revisamos si contiene claves, tokens, direcciones internas o datos de una aplicación. Para resolver un problema suele ser suficiente consultar los nombres que intervienen.

Variables de una sesión de Linux mostradas por printenv

Variables de una sesión mostradas por printenv. Conviene revisar el contenido antes de compartir una salida completa.

Una variable de Bash no siempre está en el entorno

Abrimos una shell de prueba para no mezclar las siguientes comprobaciones con la sesión donde estamos trabajando:

bash --noprofile --norc

No carga los perfiles ni el archivo de inicio habitual de Bash, pero sigue heredando el entorno del proceso que la ha abierto. No es un entorno vacío ni un aislamiento de seguridad.

Creamos una variable ficticia y consultamos su valor desde esa shell:

ESL_EJEMPLO='dos palabras'
printf '%s\n' "$ESL_EJEMPLO"

Veremos dos palabras. Ahora intentamos consultarla como variable de entorno:

printenv ESL_EJEMPLO

Si hemos creado este nombre por primera vez y no estaba exportado, printenv no lo encuentra. La variable existe en Bash, pero no se ha incluido en el entorno que recibe ese programa externo.

Esto también explica por qué algunas opciones de Bash no aparecen en printenv. Por ejemplo, HISTSIZE sirve para configurar la shell, pero no se convierte automáticamente en una variable exportada por el hecho de tener un valor.

Usar export para los programas que iniciamos después

Marcamos la variable de ejemplo para que la reciban los procesos hijos:

export ESL_EJEMPLO
printenv ESL_EJEMPLO

Ahora la consulta muestra su valor. Podemos hacer la asignación y la exportación juntas:

export ESL_EJEMPLO='valor exportado'

Si abrimos un nuevo proceso Bash desde esa sesión, también recibe el valor:

bash --noprofile --norc -c 'printf "%s\n" "$ESL_EJEMPLO"'

La explicación del entorno en Bash describe esta herencia. No estamos cambiando una tabla global que se actualice dentro de todas las aplicaciones abiertas.

Un editor que ya estaba funcionando puede conservar el entorno con el que se inició. Y otra terminal abierta desde el escritorio puede proceder de un proceso distinto, no de la shell en la que acabamos de ejecutar export.

Por eso una variable que funciona en una sesión y no en otra no se arregla necesariamente copiando su valor una y otra vez. Hay que saber desde dónde se inicia el programa que la necesita.

Definir un valor sólo para una orden

Si queremos probar un ajuste sin exportarlo para el resto de la sesión, podemos anteponer la asignación a una orden externa:

ESL_EJEMPLO='sólo para esta orden' printenv ESL_EJEMPLO

Ese proceso recibe el valor temporal. Cuando termina, la variable que tenía nuestra shell mantiene su valor anterior, si existía. Podemos comprobarlo con:

printf '%s\n' "$ESL_EJEMPLO"

En este ejemplo seguimos usando un nombre ficticio. Es más seguro aprender el mecanismo así que cambiar una variable de configuración importante y descubrir después que otra herramienta depende de ella.

Este comportamiento está explicado para la llamada a un programa externo. Funciones y ciertas órdenes internas de la shell pueden tener detalles distintos, así que no utilizaría el ejemplo como una regla sin excepciones para cualquier construcción de Bash.

Diferenciar nombre, expansión y comillas

Estas dos órdenes hacen cosas distintas:

printenv HOME
printf '%s\n' "$HOME"

En la primera, printenv recibe el nombre HOME y busca su valor en el entorno. En la segunda, Bash expande $HOME antes de ejecutar printf.

Las comillas mantienen el valor como un único argumento, aunque contenga espacios. No pasamos printenv $HOME para consultar la variable: estaríamos entregándole el contenido de la ruta como si fuera un nombre.

Consulta de la ruta personal desde una terminal de Linux

Consulta de la ruta personal desde la terminal.

Esta distinción aparece también en scripts que reciben nombres de archivo. Entrecomillar un valor no es sólo una preferencia de estilo. Cambia cómo se construyen los argumentos que recibe la orden.

Quitar la variable o dejar de exportarla

Dentro de nuestra shell de prueba podemos retirar la marca de exportación:

export -n ESL_EJEMPLO
printf '%s\n' "$ESL_EJEMPLO"
printenv ESL_EJEMPLO

El valor sigue estando disponible para Bash, pero los nuevos procesos ya no lo reciben como variable exportada. Si queremos eliminarlo también de esta shell:

unset ESL_EJEMPLO

No hemos modificado los procesos que ya estaban abiertos. Un hijo que recibió el valor antes puede seguir conservándolo. Del mismo modo, si un proceso hijo cambia su propio entorno, no cambia por esa vía el de su padre.

Para salir de la shell de prueba y volver a la anterior:

exit

Estos cambios eran de esa sesión. No hemos editado ningún archivo para hacerlos persistentes.

Hacerlo persistente sólo cuando sepamos dónde se necesita

No existe un archivo de inicio que sirva igual para todos los casos. Una terminal interactiva, una sesión de inicio, una aplicación del escritorio y un servicio pueden recibir su configuración por caminos diferentes.

En Bash, ~/.bashrc es habitual para shells interactivas que no son de inicio de sesión. Las shells de login utilizan otros archivos según las reglas de inicio de Bash. Añadir una línea a uno de ellos no significa que un servicio o todas las ventanas del escritorio vayan a leerla.

Antes de guardar una asignación permanente, haría copia del archivo correspondiente, comprobaría qué shell utilizamos y probaría en una sesión nueva. No duplicaría la misma línea en varios perfiles hasta que algo empiece a funcionar.

Si el ajuste pertenece a un servicio, revisamos su mecanismo de configuración, no lo trasladamos automáticamente al perfil personal. Y si contiene un secreto, necesitamos un lugar y unos permisos adecuados, no un archivo que terminemos subiendo con el proyecto.

Si un programa recibe un valor distinto del esperado

Primero consultamos la variable concreta desde el contexto donde iniciamos el programa. Después comprobamos si estaba exportada y si esa aplicación se abrió antes del cambio. Reiniciar el ordenador no debería ser el primer paso para entender una diferencia entre procesos.

También miramos si el programa utiliza realmente esa variable o dispone de una opción propia que tenga prioridad. Tener un valor en printenv no obliga a una aplicación a interpretarlo de la manera que esperamos.

Para pedir ayuda, guardamos el nombre, una versión del valor sin información sensible, la orden utilizada y cómo se inicia el programa. Un volcado completo del entorno suele añadir ruido y puede revelar datos que no eran necesarios.

Con una consulta concreta y una prueba temporal podemos entender la mayoría de estos casos sin ir modificando perfiles a ciegas. Lo útil es seguir el recorrido del valor: dónde se define, a qué proceso llega y qué programa lo utiliza.

Sergio Almela

Senior Software Engineer con más de diez años de experiencia. Escribo sobre Linux, software libre, backend, arquitectura, rendimiento, testing e IA aplicada al desarrollo.

View all posts by Sergio Almela →