O principal problema aqui - que explica por que, por exemplo, $PS1 não é relatado por env - é que env está relatando de um ambiente não interativo . Os processos são executados a partir de uma bifurcação do seu shell interativo , mas há uma sutileza envolvida em como o ambiente é definido: na verdade, ele é herdado por meio de um conjunto de variáveis externas nativas do nível C para todos os processos exec() d veja man environ ). Aqui está uma ilustração:
#include <stdio.h>
extern char **environ;
int main (void) {
int i;
for (i = 0; environ[i] != NULL; i++) {
printf("%s\n", environ[i]);
}
return 0;
}
O interessante é que, se você compilar e executar, o conteúdo de **environ corresponderá exatamente ao reportado por env :
$ gcc test.c
$ ./a.out > aout.txt
$ env > env.txt
$ diff env.txt aout.txt
68c68
< _=/bin/env
---
> _=./a.out
A única diferença é o nome do executável. Então, de onde vem **environ e por que não contém, por exemplo, $PS1 ?
A explicação fundamental é que o processo é sempre criado como filhos de outros processos e eles herdam **environ , mas PS1 nunca fez parte dele. Na inicialização, um shell pode fornecer variáveis a partir de locais padrão, e esses locais diferem dependendo se o shell é interativo ou não; veja INVOCATION em man bash . Um aspecto disso é que:
PS1 is set [...] if bash is interactive, allowing a shell script or a startup file to test this state.
Agora, observe em /etc/bashrc algo assim:
# are we an interactive shell?
if [ "$PS1" ]; then
Qual é o local onde o prompt real (de fantasia) está configurado e nem o valor inicial de $PS1 nunca foi export ed. O valor inicial foi criado pelo shell na chamada porque era interativo e, em seguida, originou esse arquivo, mas PS1 não foi colocado em **environ . Você pode ver isso se você executar:
#!/bin/sh
echo $PS1
Nada - mesmo que você tenha echo $PS1 em seu shell interativo definido. Isso ocorre porque o **environ do #!/bin/sh executado é o mesmo do shell interativo pai, mas NÃO contém PS1 . Isso implica que cada shell usa uma tabela interna de variáveis globais separadas, mas originalmente preenchidas, de **environ (isso é confuso, pois significa que **environ não inclui muitas coisas referidas como environment variables ).
O conteúdo de **environ está em /proc/[PID]/environ e, se você verificar que o seu shell interativo atual, cat /proc/$BASHPID/environ , verá PS1 não está lá.
Mas como as coisas entram no "environ"?
A resposta simples é, via chamadas de sistema. Por exemplo, se lançarmos algumas coisas no programa C de exemplo anterior:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **environ;
int main (void) {
int i;
if (putenv("MYFOO=whatbar?")) {
fprintf(stderr, "putenv() failed: %s\n", strerror(errno));
exit(1);
}
for (i = 0; environ[i] != NULL; i++) {
printf("%s\n", environ[i]);
}
return 0;
}
MYFOO=whatbar? estará na saída (consulte man putenv ). Como o shell cria processos por fork() ing (que duplica a pilha de memória do pai) e chamando execv() (que passa no **environ duplicado), podemos ver um mecanismo pelo qual variáveis de ambiente podem ser export ed para processos filhos.
Se você lançar um fork() nesse exemplo, verá que esse é o caso, e (para reiterar), esse processo de fork'ing e potencialmente exec'ing é como processos filhos são criados e herdam **environ de seus ancestrais. exec chamadas substituem a imagem do processo, mas conforme man execv e man environ (nb. algumas versões da primeira não se referem a isso), **environ é passado pelo sistema.
Veja um fork literal e exec de /usr/bin/env com MYFOO=whatbar? exportados via putenv() :
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
extern char **environ;
int main (void) {
pid_t pid;
if (putenv("MYFOO=whatbar?")) {
fprintf(stderr, "putenv() failed: %s\n", strerror(errno));
exit(1);
}
pid_t pid = fork();
if (!pid) execl("/usr/bin/env", "env", NULL);
return 0;
}
Então, onde estão as coisas que não estão no "environ"?
São dados privados de uma determinada instância da shell. O Bash mostrará este + o material do ambiente herdado via set sem argumentos. Observe que esta saída também inclui funções originadas.
But, if I find, for example, LC_CTYPE using env | grep "LC_CTYPE", it sends no output. In general, locale shows me 13 LC_* variables and env only nine:
Eu não obtenho nenhuma variável LC_ de env (apenas LANG ) mas 13 de locale . Eu presumo que sejam variáveis definidas por uma chamada locale e não exportadas; o fato de você obter algum de env talvez reflita um erro ingênuo em alguma configuração em algum lugar.