script bash e colisão de namespace de variável env local

4

Eu tenho um bash script com uma variável chamada VAR_A

Eu também tenho um local env variable chamado VAR_A

O bash script chama o comando:

echo ${VAR_A}

Não consigo alterar a variável no script para outro nome, mas posso alterar a forma como o script chama o comando echo .

Como posso modificar o comando echo para garantir que ele imprima o env variable local em vez daquele fornecido no script?

UPDATE FOR CLARITY:

A situação:

O usuário tem um arquivo .bashrc existente que, no login, define:

VAR_A="someValue"
export VAR_A

Isso permite que o usuário:

~]$ echo ${VAR_A}
    someValue

Eu tenho um arquivo de configuração para alguns scripts bash

VAR_A="someOtherValue"

Eu tenho um script bash:

#!/bin/bash

. ../configuration  # imports config file with some values

#  do stuff

echo ${VAR_A}

A execução do script a partir do terminal (bash shell como usuário conectado) imprime:

~]$ ./run_script.sh
    someOtherStuff

Eu preciso imprimir:

~]$ ./run_script.sh
    someStuff

Espero que ajude a esclarecer as coisas.

    
por SnakeDoc 24.03.2014 / 07:33

5 respostas

6

Variáveis do shell são inicializadas a partir de variáveis de ambiente em cada shell, você não pode contornar isso.

Quando o shell é iniciado, para cada variável de ambiente que recebe um nome válido como uma variável de shell, o shell designa a variável de shell correspondente ao valor correspondente. Por exemplo, se o seu script for iniciado como:

env VAR_A=xxx your-script

(e tem #!/bin/bash - she-bang), env executará /bin/bash e passará VAR_A=xxx nesse comando bash e bash atribuirá a variável $VAR_A o valor xxx .

No shell Bourne e no C-shell, se você atribuir um novo valor a essa variável do shell, isso não afetará a variável env correspondente passada para comandos posteriores executados por esse shell, você precisará usar export ou setenv para isso (observe que no shell Bourne se você usa unset a, ele remove tanto a variável do shell quanto a variável de ambiente).

Em:

env VAR=xxx sh -c 'VAR=yyy; other-command'

(com sh sendo o shell Bourne, não os atuais POSIX shells) ou:

env VAR=xxx csh -c 'set VAR = yyy; other-command'

other-command recebe VAR=xxx em seu ambiente, não VAR=yyy , você precisaria escrevê-lo:

env VAR=xxx sh -c 'VAR=yyy; export VAR; other-command'

ou

env VAR=yyy csh -c 'setenv VAR yyy; other-command'

Por other-command para receber VAR=yyy em seu ambiente.

No entanto, ksh (e POSIX como resultado, e então bash e todos os outros tipos de Bourne como resultado) quebraram isso.

Após o start-up, essas shells modernas ligam sua variável shell à variável de ambiente correspondente.

O que isso significa é que um script pode destruir o ambiente apenas definindo uma de suas variáveis, mesmo se não exportá-lo. Alguns shells são conhecidos por remover as variáveis de ambiente que ele não pode mapear para as variáveis do shell (por isso é recomendado usar apenas nomes de variáveis mapeáveis por shell para nomes de variáveis de ambiente).

Essa é a principal razão pela qual, por convenção, todas as variáveis maiúsculas devem ser reservadas para variáveis de ambiente.

Para contornar isso, se você quiser que os comandos executados por seu script recebam o mesmo ambiente que o interpretador de shell recebido, você precisará armazenar esse ambiente de alguma forma. Você pode fazer isso adicionando:

my_saved_env=$(export -p)

no início do seu script e, em seguida, execute seus comandos com:

(eval "$my_saved_env"; exec my-other-command and its args)
    
por 24.03.2014 / 11:19
5

Você pode tornar a variável VAR_A somente leitura adicionando uma linha:

readonly VAR_A

na parte superior do seu script. Isso faria com que o valor de VAR_A fosse preservado de acordo com seu ambiente local.

readonly: readonly [-aAf] [name[=value] ...] or readonly -p

Mark shell variables as unchangeable.

Mark each NAME as read-only; the values of these NAMEs may not be
changed by subsequent assignment.  If VALUE is supplied, assign VALUE
before marking as read-only.

Options:
  -a        refer to indexed array variables
  -A        refer to associative array variables
  -f        refer to shell functions
  -p        display a list of all readonly variables and functions

An argument of '--' disables further option processing.

Exit Status:
Returns success unless an invalid option is given or NAME is invalid.

O seguinte exemplo deve deixar claro:

$ export FOO="somevalue"        # environment variable FOO set to somevalue
$ cat test                      # test script
echo $FOO                       # print the value of FOO
readonly FOO                       # set FOO to local
FOO="something"                 # attempt to modify FOO
echo $FOO                       # print the value of FOO -- you would see the value that was inherited from the environment
$ bash test
somevalue
test: line 3: FOO: readonly variable
something
    
por 24.03.2014 / 07:45
2

Se você quiser imprimir a variável local VAR_A , deverá chamar em seu escopo local; caso contrário, imprimirá o valor da variável global VAR_A :

#! /bin/bash 

VAR_A="I'm global"

function lexical() {
    local VAR_A="I'm lexical"
    echo $VAR_A
}

echo -n "Global VAR_A: "
echo $VAR_A
echo -n "Lexical VAR_A: "
lexical

Execute:

$ ./test.sh 
Global VAR_A: I'm global
Lexical VAR_A: I'm lexical

Você pode ler mais sobre local no bash a partir de man page:

local [option] [name[=value] ...]
              For each argument, a local variable named name is  created,  and
              assigned  value.   The option can be any of the options accepted
              by declare.  When local is used within a function, it causes the
              variable  name  to have a visible scope restricted to that func‐
              tion and its children.  With no operands, local writes a list of
              local  variables  to the standard output.  It is an error to use
              local when not within a function.  The return status is 0 unless
              local  is  used outside a function, an invalid name is supplied,
              or name is a readonly variable.
    
por 24.03.2014 / 07:45
2

tl; dr:

grep -zoP '^VAR_A=\K(?s).*' "/proc/$PPID/environ"

Não está totalmente claro o que você está perguntando como um "local env variable " é uma contradição. As variáveis de ambiente são globais, não locais.

Você tem três tipos de variáveis:

  • exportar variáveis globais (variáveis de ambiente).
  • variáveis globais.
  • variáveis locais.

Variáveis globais são definidas para o processo de shell. Qualquer função de shell em execução nesse processo pode ler e modificar variáveis globais. As variáveis exportadas são colocadas no ambiente de processos e disponíveis para processos filhos. Variáveis locais são locais para uma função de shell.

O que eu acho que você está perguntando é se você pode acessar uma variável de ambiente que existe antes de invocar um script depois que o script modifica a variável.

No bash puro, você não pode fazer isso. Quando o script é executado, todas as variáveis de ambiente são exportadas para variáveis globais. Quando o script modifica VAR_A , ele é alterado no ambiente.

Se você estiver usando o Linux, poderá acessar o ambiente do pai através do sistema de arquivos proc . O processo pai geralmente terá a variável de ambiente original que você está procurando:

grep -zoP '^VAR_A=\K(?s).*' "/proc/$PPID/environ"

Isso extrairá a variável de ambiente VAR_A do ambiente do processo pai ( $PPID ) e imprimirá apenas a parte do valor. Ele depende de você ser capaz de ler o ambiente do seu pai (mesmo UID) e que o pai tem a variável que você está procurando.

    
por 24.03.2014 / 10:27
1

O que está acontecendo é que o arquivo de configuração VAR_A está sobrescrevendo o conteúdo do VAR_A proveniente do .bashrc .

Assim, a solução sugere-se: antes de você criar o arquivo de configuração, salve o conteúdo do VAR_A , como mostrado abaixo.

VAR_A_sav=${VAR_A} # assuming that VAR_A_sav does NOT exist in configuration

. ../configuration # that is, you must choose a name not existing in config.

VAR_A=${VAR_A_sav}
    
por 14.02.2017 / 16:46