Por que não consigo imprimir uma variável que posso ver na saída de env?

8

Estou interessado em definir variáveis ambientais de uma instância de shell de outra. Então eu decidi fazer alguma pesquisa. Depois de ler um número de perguntas sobre isso eu decidi testá-lo.

Eu criei dois shells A e B (PID 420), ambos rodando zsh . Do shell A eu corri o seguinte.

sudo gdb -p 420
(gdb) call setenv("FOO", "bar", 1)
(gdb) detach

Do shell B quando executo env , posso ver que a variável FOO é de fato configurada com um valor de bar. Isso me faz pensar que o FOO foi inicializado com sucesso no ambiente do shell B. No entanto, se eu tentar imprimir o FOO, recebo uma linha vazia, indicando que ela não está definida. Para mim, parece que há uma contradição aqui.

Isso foi testado em meu próprio sistema Arch GNU / Linux e em uma VM Ubuntu. Eu também testei isso em bash onde a variável nem apareceu em env. Isso, embora seja decepcionante para mim, faz sentido se o shell armazena em cache uma cópia de seu ambiente no tempo de spawn e usa apenas isso (o que foi sugerido em uma das perguntas vinculadas). Isso ainda não responde porque zsh pode ver a variável.

Por que a saída de echo $FOO empty?

EDITAR

Após a entrada nos comentários, decidi fazer um pouco mais de testes. Os resultados podem ser vistos nas tabelas abaixo. Na primeira coluna está o shell no qual a variável FOO foi injetada. A primeira linha contém o comando cuja saída pode ser vista abaixo dela. A variável FOO foi injetada usando: sudo gdb -p 420 -batch -ex 'call setenv("FOO", "bar", 1)' . Os comandos específicos para zsh: zsh -c '...' também foram testados usando o bash. Os resultados foram idênticos, sua saída foi omitida por brevidade.

Arch GNU / Linux, zsh 5.3.1, bash 4.4.12 (1)

|      |  env | grep FOO  | echo $FOO |  zsh -c 'env | grep FOO'  |  zsh -c 'echo $FOO'  |         After export FOO          |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh  |  FOO=bar         |           | FOO=bar                   | bar                  | No Change                         |
| bash |                  | bar       |                           |                      | Value of FOO visible in all tests |

Ubuntu 16.04.2 LTS, zsh 5.1.1, bash 4.3.48 (1)

|      |  env | grep FOO  | echo $FOO |  zsh -c 'env | grep FOO'  |  zsh -c 'echo $FOO'  |         After export FOO          |
|------|------------------|-----------|---------------------------|----------------------|-----------------------------------|
| zsh  |  FOO=bar         |           | FOO=bar                   | bar                  | No Change                         |
| bash |                  | bar       |                           |                      | Value of FOO visible in all tests |

O acima parece implicar que os resultados são distribuição agnóstica. Isso não me diz muito mais do que zsh e bash manipulam a configuração de variáveis de maneira diferente. Além disso, export FOO tem um comportamento muito diferente neste contexto, dependendo do shell. Espero que estes testes possam deixar algo claro para alguém.

    
por rlf 09.07.2017 / 15:46

1 resposta

1

A maioria das shells não usa a API getenv() / setenv() / putenv() .

Ao iniciar, eles criam variáveis de shell para cada variável de ambiente. Eles serão armazenados em estruturas internas que precisam carregar outras informações, como se a variável fosse exportada, somente leitura ... Eles não podem usar o environ da libc para isso.

Da mesma forma, e por esse motivo, eles não usarão execlp() , execvp() para executar comandos, mas chamarão a chamada de sistema execve() diretamente, computando a matriz envp[] com base na lista de suas variáveis exportadas.

Portanto, em seu gdb , você precisaria adicionar uma entrada a essa tabela interna de variáveis shells, ou possivelmente chamar a função correta que faria com que ela interpretasse um código export VAR=value para atualizar a tabela por si só .

Por que você vê uma diferença entre bash e zsh quando chama setenv() in gdb , suspeito que seja porque você está chamando setenv() antes do shell inicializar, por exemplo, ao digitar main() .

Você perceberá que bash main() é int main(int argc, char* argv[], char* envp[]) (e bash mapeia as variáveis dessas env vars em envp[] ), enquanto zsh é int main(int argc, char* argv[]) e zsh obtém as variáveis de environ em vez disso. setenv() modifica environ , mas não pode modificar envp[] no local (somente leitura em vários sistemas, bem como as cadeias apontadas por esses ponteiros).

Em qualquer caso, após o shell ter lido environ na inicialização, usar setenv() seria ineficaz, pois o shell não usa mais environ (ou getenv() ) posteriormente.

    
por 31.08.2017 / 18:01