C Variáveis de Ambiente: getenv
Como um programa sabe qual é a pasta pessoal de quem o está usando, ou em que idioma falar, sem perguntar nada? Ele lê o ambiente: uma lista de pares NOME=valor que todo programa recebe ao começar, como HOME=/home/ana e LANG=pt_BR.UTF-8. Nesta aula você vai ler essas variáveis com getenv, tratar a que não existe, usar o ambiente para configurar a lojinha da Ana sem mudar o código, e ver o que um programa pode (e não pode) mudar nele.
- O que são as variáveis de ambiente e como vê-las no terminal (
env,echo $HOME). - Como ler uma com
getenve por que sempre conferir oNULL. - Como dar um valor padrão e como definir uma variável só para uma execução (
NOME=valor ./prog) ou para todas (export). - Como converter e validar uma variável numérica.
- Como montar um caminho a partir do
HOME. setenv,unsetenveenviron, do POSIX, e até onde vai uma mudança no ambiente.
Você vai precisar de Ponteiro Nulo, Texto ↔ Número e stdin, stdout e stderr. Todos os programas foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c, com gcc -std=c23 -Wall -Wextra arquivo.c -o programa.
A ideia: o quadro de avisos da cozinha
Na parede da cozinha da lojinha há um quadro de avisos: "forno: 180 graus", "cliente do dia: dona Maria", "hoje fecha às 18h". Todo funcionário que chega dá uma olhada no quadro e trabalha de acordo, sem que a Ana precise repetir para cada um. E, se a Ana quiser que só o confeiteiro de hoje use 200 graus, ela entrega a ele uma cópia do quadro com essa linha trocada.
O ambiente funciona assim. O terminal tem o seu quadro de avisos e, quando roda um programa, entrega a ele uma cópia. O programa lê o que quiser da cópia, pode até rabiscá-la, mas o quadro da parede (o do terminal) continua igual.
1. O ambiente da Ana
No terminal, o comando env mostra o ambiente inteiro:
$ env SHELL=/bin/bash LANGUAGE=pt_BR:pt:en EDITOR=nano PWD=/home/ana LOGNAME=ana TZ=America/Sao_Paulo HOME=/home/ana LANG=pt_BR.UTF-8 TERM=xterm-256color TERMINFO=/usr/share/terminfo USER=ana SHLVL=1 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin _=/usr/bin/env
Cada linha é uma variável: o nome (em maiúsculas, por costume), um = e o valor, que é sempre texto. As mais úteis:
| Variável | Na máquina da Ana | O que é |
|---|---|---|
HOME | /home/ana | A pasta pessoal de quem está usando |
USER | ana | O nome da usuária |
PATH | /usr/local/sbin:…:/bin | As pastas onde o shell procura os comandos, separadas por : |
LANG | pt_BR.UTF-8 | Idioma e codificação (o setlocale(LC_ALL, "") da aula errno lê esta) |
PWD | /home/ana | A pasta atual (quando o terminal abriu) |
EDITOR | nano | O editor de texto preferido |
TZ | America/Sao_Paulo | O fuso horário |
Para ver uma só, use echo $NOME (o $ pede ao shell o valor) ou printenv NOME:
$ echo $HOME /home/ana $ printenv USER ana $ printenv LOJA_MOEDA; echo $? 1 « não existe: nada impresso, código 1
2. getenv: ler uma variável
getenv = "get environment", pegar do ambiente. Você passa o nome e recebe o valor, ou NULL se a variável não existe:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *casa = getenv("HOME");
const char *usuaria = getenv("USER");
const char *moeda = getenv("LOJA_MOEDA");
printf("HOME = %s\n", casa != NULL ? casa : "(não existe)");
printf("USER = %s\n", usuaria != NULL ? usuaria : "(não existe)");
printf("LOJA_MOEDA = %s\n", moeda != NULL ? moeda : "(não existe)");
return 0;
}
- #include <stdlib.h>
- Onde mora o
getenv. Ele existe desde o primeiro padrão do C (C89). - getenv("HOME")
- Procura
HOMEno ambiente. O nome é exato:"home", em minúsculas, é outra variável (que não existe). - const char *casa
- O
getenvdevolve um ponteiro para o texto do valor, que mora no ambiente do programa. Oconsté uma promessa sua: "não vou mudar esse texto" (seção 5). - casa != NULL ? casa : "(não existe)"
- O operador ternário: se a variável existe, mostra o valor; senão, um texto no lugar. Nunca passe um possível
NULLdireto para oprintf(veja em Quando dá errado).
$ gcc -std=c23 -Wall -Wextra ambiente.c -o ambiente $ ./ambiente HOME = /home/ana USER = ana LOJA_MOEDA = (não existe) $ LOJA_MOEDA=BRL ./ambiente HOME = /home/ana USER = ana LOJA_MOEDA = BRL
Na segunda vez, o LOJA_MOEDA=BRL antes do nome do programa acrescentou uma linha à cópia do quadro, só para aquela execução. É assim que se configura um programa pelo ambiente (seção 3).
HOME=/home/ana, USER=ana, LANG=pt_BR.UTF-8, PATH etc. Não há como acrescentar variáveis antes de rodar (não existe o NOME=valor do terminal), então as caixas desta aula usam as que já estão lá, ou o setenv da seção 6.
Missão: o programa cumprimenta sempre um "visitante". Faça-o cumprimentar quem está usando o computador: leia a variável USER com getenv e, se ela não existir (NULL), continue usando "visitante". No editor, a saída deve ser Olá, ana!.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *nome = "visitante";
// leia USER aqui
printf("Olá, %s!\n", nome);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *nome = "visitante";
const char *usuario = getenv("USER");
if (usuario != NULL) {
nome = usuario;
}
printf("Olá, %s!\n", nome);
return 0;
}
3. Valor padrão e NOME=valor ./programa
O uso mais comum: uma configuração opcional. Se a variável existe, o programa a usa; se não, usa um valor padrão. Assim, a Ana pode mudar o nome da loja que aparece na tela sem recompilar nada:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *nome = getenv("LOJA_NOME");
if (nome == NULL || nome[0] == '\0') {
nome = "Lojinha";
}
printf("Bem-vindo à %s!\n", nome);
return 0;
}
- nome == NULL
- A variável não existe.
- || nome[0] == '\0'
- Ou existe, mas está vazia (
LOJA_NOME= ./boasvindas). O||só olha onome[0]se onomenão forNULL, então é seguro. - nome = "Lojinha";
- O valor padrão. Ele é um texto fixo do programa, por isso o
nomeéconst char *: tanto o texto dogetenvquanto o padrão não devem ser alterados.
No terminal, há três jeitos de entregar a variável ao programa:
$ gcc -std=c23 -Wall -Wextra boasvindas.c -o boasvindas $ ./boasvindas Bem-vindo à Lojinha! $ LOJA_NOME="Padaria da Ana" ./boasvindas Bem-vindo à Padaria da Ana! $ echo "[$LOJA_NOME]" [] « valeu só para aquela execução $ LOJA_NOME="Doceria" $ ./boasvindas Bem-vindo à Lojinha! « variável do shell, sem export: não chega $ export LOJA_NOME $ ./boasvindas Bem-vindo à Doceria! « com export: chega a todo programa daqui em diante $ unset LOJA_NOME $ ./boasvindas Bem-vindo à Lojinha!
| No terminal | Efeito |
|---|---|
NOME=valor ./prog | Só esta execução do prog recebe a variável |
NOME=valor (sozinho) | Cria uma variável do shell, que os programas não recebem |
export NOME ou export NOME=valor | Põe a variável no ambiente: todos os programas rodados depois, neste terminal, a recebem |
unset NOME | Apaga a variável |
Para a variável valer sempre, em todo terminal novo, a Ana escreveria o export no arquivo ~/.bashrc (o Guia Bash explica). E, quando você escolher nomes para as variáveis do seu programa, use um prefixo (LOJA_), para não se confundir com as do sistema.
4. Números no ambiente: converter e validar
O valor de uma variável é sempre texto, e quem o escreveu foi uma pessoa: pode estar errado. Trate-o como qualquer entrada (aula Validar a Entrada): converta com strtol, confira, e, se não servir, avise no stderr e use o padrão.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
long desconto = 0;
const char *texto = getenv("LOJA_DESCONTO");
if (texto != NULL) {
char *fim;
errno = 0;
long n = strtol(texto, &fim, 10);
if (fim == texto || *fim != '\0' || errno == ERANGE || n < 0 || n > 50) {
fprintf(stderr, "aviso: LOJA_DESCONTO=\"%s\" não vale (use 0 a 50); sem desconto\n", texto);
} else {
desconto = n;
}
}
double preco = 20.0;
printf("Bolo: R$ %.2f\n", preco * (100 - desconto) / 100);
return 0;
}
- long desconto = 0;
- Começa com o padrão. Só muda se a variável existir e for válida.
- if (texto != NULL)
- Variável ausente não é erro: é o caso normal, e o padrão vale.
- fim == texto || *fim != '\0' || errno == ERANGE || n < 0 || n > 50
- As conferências de sempre: nenhum dígito, sobra de texto, número grande demais, fora da faixa.
- fprintf(stderr, "aviso: …")
- Avisa, mas não para o programa: uma configuração errada não deve impedir a loja de abrir. Mostrar o valor recebido (
"%s") ajuda a pessoa a achar o erro de digitação.
$ gcc -std=c23 -Wall -Wextra desconto.c -o desconto $ ./desconto Bolo: R$ 20.00 $ LOJA_DESCONTO=10 ./desconto Bolo: R$ 18.00 $ LOJA_DESCONTO=dez ./desconto aviso: LOJA_DESCONTO="dez" não vale (use 0 a 50); sem desconto Bolo: R$ 20.00 $ LOJA_DESCONTO=80 ./desconto aviso: LOJA_DESCONTO="80" não vale (use 0 a 50); sem desconto Bolo: R$ 20.00
5. O texto devolvido: não mude, copie se for guardar
O ponteiro do getenv aponta para dentro do ambiente do programa. Duas regras:
- Não escreva nele. Mudar o texto é comportamento indefinido. Por isso, guarde-o num
const char *: se você tentar mudá-lo sem querer, o compilador reclama. - Copie, se for usar muito depois. O padrão C permite que uma nova chamada ao
getenv(ou umsetenv) mude o texto. Se você precisa montar algo com o valor, monte a sua própria string.
O caso típico é montar o caminho de um arquivo na pasta pessoal, como fazem os programas que guardam configurações em ~/.algumacoisa:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *casa = getenv("HOME");
if (casa == NULL) {
fprintf(stderr, "HOME não está definida\n");
return 1;
}
char caminho[256];
int tam = snprintf(caminho, sizeof caminho, "%s/vendas.txt", casa);
if (tam < 0 || tam >= (int)sizeof caminho) {
fprintf(stderr, "caminho comprido demais\n");
return 1;
}
printf("Vou gravar em %s\n", caminho);
FILE *f = fopen(caminho, "a");
if (f == NULL) {
perror(caminho);
return 1;
}
fprintf(f, "3 pães\n");
fclose(f);
return 0;
}
- snprintf(caminho, sizeof caminho, "%s/vendas.txt", casa)
- Monta
/home/ana/vendas.txtnuma string sua, com limite de tamanho (aula Funções de String). Nunca usestrcatdireto no texto dogetenv: ele não é seu e não tem espaço sobrando. - tam >= (int)sizeof caminho
- O
snprintfdevolve o tamanho que o texto teria. Se não coube, ele foi cortado: melhor parar do que gravar no lugar errado. OHOMEvem de fora e pode ser enorme. - fopen(caminho, "a")
- "a" de append: acrescenta no fim do arquivo (aula Escrever Arquivos).
$ gcc -std=c23 -Wall -Wextra caminho.c -o caminho $ ./caminho Vou gravar em /home/ana/vendas.txt $ cat ~/vendas.txt 3 pães $ HOME=/tmp ./caminho Vou gravar em /tmp/vendas.txt $ env -u HOME ./caminho HOME não está definida
O env -u HOME roda o programa com uma cópia do ambiente sem o HOME, para testar o caminho do erro. O HOME=/tmp mostra que o programa obedece ao ambiente: é exatamente o que se quer (e também o motivo para nunca confiar cegamente nele num programa que roda com permissões especiais).
Experimente: rode e veja o ambiente do editor. Depois troque os nomes: "PATH", "LANG", "SHELL", "home" (minúsculo) e um nome inventado.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *casa = getenv("HOME");
const char *usuaria = getenv("USER");
const char *moeda = getenv("LOJA_MOEDA");
printf("HOME = %s\n", casa != NULL ? casa : "(não existe)");
printf("USER = %s\n", usuaria != NULL ? usuaria : "(não existe)");
printf("LOJA_MOEDA = %s\n", moeda != NULL ? moeda : "(não existe)");
return 0;
}
6. setenv, unsetenv e environ (POSIX)
O C padrão só tem o getenv: ler. Para mudar o ambiente, o Linux e o macOS oferecem funções do POSIX (o padrão dos sistemas do tipo Unix): setenv e unsetenv. Como não são do C, com -std=c23 elas só aparecem se você pedir, com #define _POSIX_C_SOURCE 200809L antes dos #include (como o getline da aula Ler uma Linha):
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
int main(void) {
printf("antes: %s\n", getenv("LOJA_NOME") != NULL ? getenv("LOJA_NOME") : "(não existe)");
if (setenv("LOJA_NOME", "Padaria da Ana", 1) != 0) {
perror("setenv");
return 1;
}
printf("depois do setenv: %s\n", getenv("LOJA_NOME"));
unsetenv("LOJA_NOME");
printf("depois do unsetenv: %s\n", getenv("LOJA_NOME") != NULL ? getenv("LOJA_NOME") : "(não existe)");
return 0;
}
- #define _POSIX_C_SOURCE 200809L
- "Quero também as funções do POSIX de 2008." Tem de vir antes de qualquer
#include. - setenv("LOJA_NOME", "Padaria da Ana", 1)
- Cria ou troca a variável. O
1final quer dizer "se já existir, substitua"; com0, uma variável que já existe fica como está. Devolve 0 se deu certo. - unsetenv("LOJA_NOME")
- Apaga a variável.
$ gcc -std=c23 -Wall -Wextra muda.c -o muda $ ./muda antes: (não existe) depois do setenv: Padaria da Ana depois do unsetenv: (não existe) $ echo "[$LOJA_NOME]" [] « o terminal não mudou
O setenv rabiscou a cópia do quadro: vale para o próprio programa e para os programas que ele iniciar depois, mas o terminal que o chamou não fica sabendo. Não existe jeito de um programa mudar o ambiente do shell que o rodou. (É por isso que o cd e o export são comandos do próprio shell, e não programas separados.)
Para ver o ambiente inteiro de dentro do programa, o POSIX oferece a variável global environ: um array de strings "NOME=valor" terminado em NULL, como o argv (aula Argumentos):
#include <stdio.h>
extern char **environ;
int main(void) {
for (char **p = environ; *p != NULL; p++) {
printf("%s\n", *p);
}
return 0;
}
$ gcc -std=c23 -Wall -Wextra listar.c -o listar $ env -i LOJA_NOME=Doceria LOJA_DESCONTO=10 ./listar LOJA_NOME=Doceria LOJA_DESCONTO=10 $ ./listar | wc -l 15
- extern char **environ;
- "Existe uma variável
environ, definida em outro lugar" (na biblioteca C; aula static e extern). Ela não está em nenhum cabeçalho do C padrão, por isso a declaração. - env -i …
- No terminal: roda o programa com um ambiente vazio (
-i, "ignore"), mais as variáveis escritas depois. Útil para testar.
Você também pode encontrar int main(int argc, char *argv[], char *envp[]), com um terceiro parâmetro que é o mesmo array. Ele funciona no Linux, no macOS e no Windows, mas não faz parte do padrão C; prefira getenv para ler uma variável e environ para listar.
Formas diferentes, e qual usar
| Para configurar… | Use | Exemplo |
|---|---|---|
| Algo que muda a cada execução | Argumento | ./tabuada 7 |
| Uma preferência que vale para vários programas ou várias execuções | Variável de ambiente | EDITOR=nano, LANG=pt_BR.UTF-8 |
| Muitas opções, guardadas entre execuções | Um arquivo de configuração (o caminho, muitas vezes, a partir do HOME) | ~/.lojinha.conf |
| Uma resposta da pessoa, na hora | Perguntar (Validar a Entrada) | Quantos pães? |
Muitos programas combinam os três primeiros, nesta ordem de força: o argumento ganha da variável de ambiente, que ganha do arquivo, que ganha do padrão escrito no código.
Quando dá errado
Falha de segmentação (ou (null)) ao mostrar uma variável que não existe
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *moeda = getenv("LOJA_MOEDA");
printf("%s\n", moeda);
return 0;
}
$ gcc -std=c23 -Wall -Wextra nulo2.c -o nulo2 $ ./nulo2 Falha de segmentação (imagem do núcleo gravada) ./nulo2 $ ./nulo Moeda: (null) A moeda é (null).
O que significa: o getenv devolveu NULL, e o programa o passou para o printf sem conferir. Passar NULL para um %s é comportamento indefinido, e o resultado varia: o nulo.c, com printf("Moeda: %s\n", getenv("LOJA_MOEDA")), mostra (null); o nulo2.c morre, porque o gcc troca o printf("%s\n", moeda) por um puts(moeda), mais rápido, que não tolera o NULL. No editor do guia, o nulo2.c mostra (null) e termina com 0, o que esconde o erro.
Como consertar: confira o NULL antes de usar, e dê um valor padrão (seção 3).
$ LOJA_NOME="Doceria" $ ./boasvindas Bem-vindo à Lojinha!
O que significa: NOME=valor sozinho numa linha cria uma variável do shell, que fica só no terminal. Os programas recebem só as variáveis exportadas.
Como consertar: export LOJA_NOME (ou export LOJA_NOME="Doceria"), ou LOJA_NOME="Doceria" ./boasvindas na mesma linha.
bash: LOJA_NOME: comando não encontrado
$ LOJA_NOME = Ana ./boasvindas bash: LOJA_NOME: comando não encontrado
O que significa: com espaços em volta do =, o shell entendeu LOJA_NOME como o nome de um programa, com os argumentos =, Ana e ./boasvindas.
Como consertar: sem espaços: LOJA_NOME=Ana ./boasvindas. Para um valor com espaço, use aspas: LOJA_NOME="Padaria da Ana".
error: implicit declaration of function ‘setenv’; did you mean ‘getenv’?
$ gcc -std=c23 -Wall -Wextra semposix.c -o semposix
semposix.c: In function ‘main’:
semposix.c:5:5: error: implicit declaration of function ‘setenv’; did you mean ‘getenv’? [-Wimplicit-function-declaration]
5 | setenv("LOJA_NOME", "Padaria da Ana", 1);
| ^~~~~~
| getenv
O que significa: o setenv é do POSIX, e não do C. Com -std=c23, o stdlib.h só mostra o que é do C. No editor, o Clang diz use of undeclared identifier 'setenv'; did you mean 'getenv'?.
Como consertar: #define _POSIX_C_SOURCE 200809L na primeira linha, antes dos #include.
setenv, mas no terminal ela continua igual
O que significa: nada de errado: o programa tem a sua própria cópia do ambiente (seção 6). Mudanças valem para ele e para os programas que ele iniciar.
Como consertar: para mudar o ambiente do terminal, use export no próprio terminal. Um programa pode, no máximo, escrever um comando export … no stdout para o shell executar, mas isso é coisa do Guia Bash.
Referência rápida
| Escrita | Onde | O que faz |
|---|---|---|
const char *v = getenv("NOME"); | C (stdlib.h) | O valor da variável, ou NULL se não existe |
if (v == NULL) v = "padrão"; | C | Valor padrão |
setenv("NOME", "valor", 1) | POSIX (stdlib.h + _POSIX_C_SOURCE) | Cria ou troca; 0 = deu certo |
unsetenv("NOME") | POSIX | Apaga |
extern char **environ; | POSIX | A lista inteira, terminada em NULL |
env, printenv NOME, echo $NOME | Terminal | Mostram o ambiente / uma variável |
NOME=valor ./prog | Terminal | Só para esta execução |
export NOME=valor, unset NOME | Terminal | Para todos os programas seguintes / apaga |
env -i A=1 ./prog, env -u NOME ./prog | Terminal | Ambiente vazio (mais A) / sem NOME |
Exercícios
1. A pasta pessoal
Complete para ler a pasta pessoal de quem está usando:
2. Não existe
O que o getenv devolve quando a variável não existe?
3. Para todos os programas
Complete o comando do terminal que faz todos os programas seguintes receberem LOJA_NOME:
4. O programa da Ana chama setenv("LOJA_NOME", "X", 1) e termina. Depois, no mesmo terminal, ela digita echo $LOJA_NOME. O que aparece?
5. Qual é o jeito certo de usar o valor de getenv("LOJA_MOEDA") num printf?
6. LOJA_DESCONTO=dez ./desconto. O que um programa bem-feito faz?
Missão final: a configuração da lojinha
Missão: complete a função ler_numero_env: ela lê a variável nome com getenv(nome) e devolve o número, se ele for válido e estiver entre min e max. Se a variável não existe, devolve padrao calada; se existe mas não vale, mostra no stderr aviso: LOJA_CAIXAS="muitos" não vale; usando 1 (com o nome, o valor e o padrão) e devolve padrao. Como o editor não deixa definir variáveis antes de rodar, o main começa com dois setenv, no lugar do LOJA_DESCONTO=15 LOJA_CAIXAS=muitos ./loja do terminal. A saída deve ser:
Desconto: 15% aviso: LOJA_CAIXAS="muitos" não vale; usando 1 Caixas: 1 Mesas: 4
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
// Lê a variável "nome" como um número de min a max.
// Ausente: devolve padrao. Inválida: avisa no stderr e devolve padrao.
long ler_numero_env(const char *nome, long padrao, long min, long max) {
// complete aqui
return padrao;
}
int main(void) {
// no terminal seria: LOJA_DESCONTO=15 LOJA_CAIXAS=muitos ./loja
setenv("LOJA_DESCONTO", "15", 1);
setenv("LOJA_CAIXAS", "muitos", 1);
printf("Desconto: %ld%%\n", ler_numero_env("LOJA_DESCONTO", 0, 0, 50));
printf("Caixas: %ld\n", ler_numero_env("LOJA_CAIXAS", 1, 1, 5));
printf("Mesas: %ld\n", ler_numero_env("LOJA_MESAS", 4, 1, 20));
return 0;
}
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
// Lê a variável "nome" como um número de min a max.
// Ausente: devolve padrao. Inválida: avisa no stderr e devolve padrao.
long ler_numero_env(const char *nome, long padrao, long min, long max) {
const char *texto = getenv(nome);
if (texto == NULL) {
return padrao;
}
char *fim;
errno = 0;
long n = strtol(texto, &fim, 10);
if (fim == texto || *fim != '\0' || errno == ERANGE || n < min || n > max) {
fprintf(stderr, "aviso: %s=\"%s\" não vale; usando %ld\n", nome, texto, padrao);
return padrao;
}
return n;
}
int main(void) {
// no terminal seria: LOJA_DESCONTO=15 LOJA_CAIXAS=muitos ./loja
setenv("LOJA_DESCONTO", "15", 1);
setenv("LOJA_CAIXAS", "muitos", 1);
printf("Desconto: %ld%%\n", ler_numero_env("LOJA_DESCONTO", 0, 0, 50));
printf("Caixas: %ld\n", ler_numero_env("LOJA_CAIXAS", 1, 1, 5));
printf("Mesas: %ld\n", ler_numero_env("LOJA_MESAS", 4, 1, 20));
return 0;
}
O %% no printf escreve um % de verdade (aula Formatos do printf). No laboratório, sem os setenv e com as variáveis vindas do terminal, o programa se comporta igual:
$ gcc -std=c23 -Wall -Wextra loja.c -o loja $ LOJA_DESCONTO=15 LOJA_CAIXAS=muitos ./loja Desconto: 15% aviso: LOJA_CAIXAS="muitos" não vale; usando 1 Caixas: 1 Mesas: 4 $ ./loja Desconto: 0% « nenhuma variável: todos os padrões Caixas: 1 Mesas: 4
Resumo
- O ambiente é uma lista de
NOME=valor(sempre texto) que cada programa recebe, em cópia, de quem o chamou:HOME,USER,PATH,LANG… getenv("NOME")(dostdlib.h) devolve o valor ouNULL. Confira sempre oNULLe tenha um valor padrão.- No terminal:
NOME=valor ./progvale para uma execução;exportvale para os programas seguintes; uma variável do shell semexportnão chega ao programa. - O valor veio de uma pessoa: converta com
strtol, valide e, se não servir, avise nostderre use o padrão. - Não altere o texto do
getenv: guarde numconst char *e monte as suas strings comsnprintf. setenv/unsetenv/environsão do POSIX (#define _POSIX_C_SOURCE 200809L) e mudam só a cópia do próprio programa, nunca o terminal.