setting environment variable como uma função de outra variável env [duplicada]

3

Eu gostaria de definir uma variável de ambiente (que chamaremos de "TEST") para armazenar o caminho para um subdiretório do meu diretório pessoal: algo como /home/myself/dir/ . Eu gostaria de usar a variável $HOME para que o sistema possa obter um caminho que seja válido para qualquer usuário no sistema (desde que tenha um diretório chamado "dir" em sua pasta pessoal). Eu tentei várias possibilidades, mas não consegui fazê-lo funcionar. Aqui está uma das minhas tentativas:

export TEST='$HOME/dir'

Podemos verificar se a variável foi realmente criada:

env | grep TEST

Que retorna:

TEST=$HOME/bin

Até aí tudo bem. No entanto, é aí que as coisas ficam difíceis:

cd $TEST

Retorna:

bash: cd: $HOME/bin: No such file or directory

No entanto seguintes trabalhos variantes:

eval cd $TEST

Existe alguma maneira de obter o mesmo resultado, mas sem invocar eval ?

    
por Gael Lorieul 15.03.2014 / 11:55

3 respostas

2

Você pode fazer isso, mas requer pelo menos duas avaliações de variáveis de shell e, normalmente, tudo o que você consegue é um. Isso é por design - quero dizer, o shell é onde estão todas as suas coisas, então é melhor limitar o mínimo ao inesperado e evitar abrir as portas atrás das portas se você puder evitar. Em qualquer caso, você definitivamente tem à sua disposição o eval shell embutido:

% one=two ; two=three ; eval echo \$$one
> three

% eval "one=\$$two" ; echo $one
> three

Não armazena one - > two pointer, mas ele avalia seus argumentos como uma única instrução de shell. Se isso é um pouco claro para você, você não está sozinho. Isso basicamente significa que o shell executa a mesma linha duas vezes, ou pelo menos o mais próximo possível, e todas as atribuições do segundo passe são afetadas pelas expansões do primeiro passo - o que normalmente não é o caso, como você demonstra em sua pergunta. Isso também significa que o valor literal de two de um determinado ponto no tempo é armazenado em one - copiado, não vinculado, portanto:

% one=two ; two=three
% eval echo \$$one ; two=four ; eval echo \$$one
> three
> three
% echo $two
> four

Mas o que há com a barra invertida? Bem, é aqui que fica confuso - porque você está fazendo duas passagens você tem que citar suas citações. A primeira passagem expande $one para two e retira o primeiro nível de cotações, portanto, bye-bye \ backslash, mas o sinal de dólar entre aspas permanece e assim $two se torna three na segunda passagem. Isso fica muito louco muito rápido .

As citações da Shell são difíceis o suficiente para lidar da forma como estão - apenas tente citar uma citação simples. Camada-los e você está pedindo problemas, tira, avaliar, tira, avaliar e você está indo nada seguro - porque as citações estão lá para proteger os valores de parâmetros de shell e variáveis de ambiente e shell de seus valores.

Então, o que você fez é armazenar um literal $ cifrão dentro do valor de sua variável. Você não armazenou um caminho para $HOME porque o shell não expandiu seu valor durante a atribuição - novamente, por design .

% dir=some_dir ; eval "$dir=\$HOME" ; echo $some_dir 
> /your/$HOME/path/

Você não precisa perguntar muito sobre essas coisas antes de ouvir o mal eval . É comumente chamado assim e por boas razões. Olhe:

two=var\ \;rm\ -f\ /all/your/stuff ; eval home=$two 

Acima de casa é atribuída a expansão de $ dois. A porção home= da declaração é avaliada duas vezes - após a primeira passagem e antes de a segunda, home brevemente é igual a "$ two"; mas no segundo, depois que as aspas são retiradas, $home expande para var e /all/your/stuff é igual a 0. Esse também é um exemplo de citação super básico, pode dar errado situação complicada - e apenas a exigência de eval tende a ser complicada.

Existem outras maneiras de fazer isso - o GNOUC observa a designação de inversão ${! bash específica } (acho que é assim que é chamada ...) que provavelmente faz a mesma coisa, mas de uma forma que bash tentou salvaguardar. Isso é uma coisa boa. Você também pode - e provavelmente é uma boa ideia - escrever uma função para cunhar os valores que deseja quando é chamada:

_home() { exec 2>/dev/null ; set -- /home/*/ ; for h do  (
            cd "$h" && cd "$dir" && printf %s\n "$dir" ) 
        done
} ; var="$(_home)" ; echo "$var"
> probably...
> the...
> result...
> you...
> wanted...

Quase tão perigoso quanto eval pode ser um pouco de imaginação em combinação com o uso do . do shell também. Como o trabalho de . é executar comandos no ambiente de shell atual em vez de subclassificar como os pipes e etc, é possível construir uma instrução avaliada duas vezes que pode afetar suas variáveis de ambiente com pouco confusão.

Por exemplo, se você não citar o limitador em um here-document , seu conteúdo estará sujeito à expansão do shell. Se você levar isso mais adiante e . source, você pode executar os resultados de suas expansões. Assim:

% . <<HERE /dev/stdin
> ${one=two}three=four
> home='$HOME'
> HERE
% [ "$home" = "$HOME" ] && echo $twothree
> four

Veja, desta forma, pelo menos, as aspas são mais fáceis porque elas não são removidas, exceto de dentro de uma expansão, mas elas ainda podem ser complicadas quando suas expansões contêm citações. De qualquer forma, isso envolve o que eu sei sobre o assunto.

Na verdade, mais uma coisa, e isso é super arriscado, mas é o seu computador:

 % var=eval\ echo\ \$HOME
 % $var
 > /YOUR/HOME

Mas ...

 % cd eval echo $HOME 

obviamente não funciona ...

    
por 15.03.2014 / 16:08
0

Isso não é possível em geral porque as variáveis de ambiente não funcionam dessa maneira. Aplicativos usam seu conteúdo literalmente. Você precisa de um aplicativo que viole essa regra.

Talvez seja possível usar um script de wrapper para chamar esse aplicativo. O script wrapper pode resolver o problema usando eval.

Se isso não for para um único aplicativo, você poderá definir essa variável nos arquivos de inicialização do shell.

Isso não resolverá todos os problemas possíveis. Por exemplo. ele não funcionará com sudo , cron e assim por diante (a menos que você chame um shell ou shell script).

    
por 15.03.2014 / 13:18
0

O problema é que você atribuiu a TEST string $HOME/dir , quando o shell expande TEST variable, ele não expande $ HOME. A solução simples é deixar o shell expandir $HOME quando você atribuiu a TEST :

export TEST="$HOME/dir"

Eu não sei por que você quer atrasar a expansão de $HOME , talvez você queira usar TEST como variável global e para cada usuário, $TEST será expandido para corrigir sua pasta pessoal. Nesse caso, você pode usar a expansão indireta do bash, mas somente para $HOME :

$ export TEST="HOME"
$ echo ${!TEST}
/home/cuonglm

Então você pode usar:

cd ${!TEST}/dir
    
por 15.03.2014 / 13:45