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.