Las variables de entorno son la forma más común de configurar un programa sin tocar su código. En C se leen con getenv, se cambian con setenv y se recorren con environ. Aquí verás cómo leerlas con valores por omisión y validación, y qué precauciones tomar con los secretos.
También te puede interesar
Leer una variable: getenv
getenv, de <stdlib.h>, devuelve el valor de una variable de entorno como cadena, o nullptr si no existe. La forma habitual de configurar un programa es leer unas cuantas variables, con un valor por omisión para cada una y comprobando que el valor tenga sentido:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Lee un entero de una variable de entorno, con un valor por omisión y un rango válido. */
static long entorno_entero(const char *nombre, long omision, long min, long max)
{
const char *v = getenv(nombre);
if (v == nullptr || *v == '\0') return omision;
char *fin;
long n = strtol(v, &fin, 10);
if (*fin != '\0' || n < min || n > max) {
fprintf(stderr, "Aviso: %s=«%s» no es válido; uso %ld\n", nombre, v, omision);
return omision;
}
return n;
}
int main(void)
{
const char *modo = getenv("APP_MODO");
if (modo == nullptr) modo = "desarrollo";
long puerto = entorno_entero("APP_PUERTO", 8080, 1, 65535);
long hilos = entorno_entero("APP_HILOS", 4, 1, 64);
bool hay_clave = getenv("APP_CLAVE_API") != nullptr;
printf("modo=%s puerto=%ld hilos=%ld clave_api=%s\n", modo, puerto, hilos, hay_clave ? "definida" : "falta");
return 0;
}./config
salidamodo=desarrollo puerto=8080 hilos=4 clave_api=falta
APP_MODO=produccion APP_PUERTO=443 APP_CLAVE_API=xxxx ./config
salidamodo=produccion puerto=443 hilos=4 clave_api=definida
APP_PUERTO=80a APP_HILOS=500 ./config
salidaAviso: APP_PUERTO=«80a» no es válido; uso 8080 Aviso: APP_HILOS=«500» no es válido; uso 4 modo=desarrollo puerto=8080 hilos=4 clave_api=falta
Tres detalles. Distinguir «no existe» (nullptr) de «existe y está vacía» (""), que no es lo mismo. Validar el contenido como cualquier otra entrada: un puerto «80a» o 500 hilos no deben aceptarse en silencio. Y no imprimir nunca los secretos: el programa solo dice si la clave está definida.
Cambiar el entorno: setenv y unsetenv
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
setenv("SALUDO", "hola", 1);
printf("SALUDO=%s\n", getenv("SALUDO"));
setenv("SALUDO", "adiós", 0);
printf("Sin sobrescribir: %s\n", getenv("SALUDO"));
setenv("SALUDO", "adiós", 1);
printf("Sobrescribiendo: %s\n", getenv("SALUDO"));
unsetenv("SALUDO");
printf("Tras unsetenv: %s\n", getenv("SALUDO") ? getenv("SALUDO") : "(no existe)");
return 0;
}salidaSALUDO=hola Sin sobrescribir: hola Sobrescribiendo: adiós Tras unsetenv: (no existe)
setenv y unsetenv son de POSIX. El tercer argumento de setenv dice si se sobrescribe un valor que ya existe. El cambio afecta al propio programa y a los procesos que lance después (lo heredan), nunca al intérprete de órdenes que lo ejecutó. Un aviso importante: setenv no es segura si otros hilos están leyendo el entorno a la vez, así que la configuración se lee al arrancar, antes de crear hilos. Y el puntero que devuelve getenv puede dejar de ser válido después de un setenv: si necesitas el valor más tarde, cópialo.
Ver todas las variables
#include <stdio.h>
#include <string.h>
extern char **environ;
int main(void)
{
int n = 0, de_app = 0;
for (char **e = environ; *e != nullptr; e++) {
n++;
if (strncmp(*e, "APP_", 4) == 0) {
printf("%s\n", *e);
de_app++;
}
}
printf("%s; %d son de la aplicación\n", n > 0 ? "Hay variables" : "No hay variables", de_app);
return 0;
}salidaAPP_MODO=pruebas APP_REGION=sa-east-1 Hay variables; 2 son de la aplicación
environ es un array de cadenas con la forma NOMBRE=valor, terminado en nullptr, como argv. (El programa no imprime el total porque el número de variables cambia de un sistema a otro.)
Secretos y programas con privilegios
Las variables de entorno son la forma recomendada de pasar configuración a un programa en contenedores y servicios en la nube, pero no son un lugar seguro para los secretos: otros procesos del mismo usuario pueden leerlas (en Linux, en /proc/<pid>/environ), aparecen en los volcados de memoria y se heredan en todos los procesos hijos. Para claves importantes, mejor un archivo con permisos restringidos o un gestor de secretos.
Y en un programa que se ejecuta con más privilegios que quien lo lanza (con el bit SUID), el entorno lo controla un usuario sin privilegios: hay que leerlo con secure_getenv, que devuelve nullptr en ese caso. El fallo Looney Tunables de glibc, en 2023, venía justo de ahí. Más en Seguridad en C.
Todo el código de esta página se compiló con GCC 15.3 (C23, -Wall -Wextra, sin avisos) y se ejecutó en Linux, en un contenedor limpio sin acceso a la red, antes de publicarla; las salidas están copiadas de esa ejecución.