É possível imprimir o conteúdo do conteúdo de uma variável com shell script? (referência indireta)

13

Vamos supor que eu tenha declarado as seguintes variáveis:

$ var='$test'
$ test="my string"

Se eu imprimir o conteúdo, vejo o seguinte:

$ echo $var
$test

$ echo $test
my string

Gostaria de encontrar uma maneira de imprimir o conteúdo do conteúdo de $var(qual é o conteúdo de $test). Então, tentei fazer o seguinte:

$ echo $(echo $var)
$test

Mas aqui o resultado é $teste não "my string"... É possível imprimir o conteúdo do conteúdo de variáveis ​​usando o bash?

Rafael Muynarsk
fonte
Você poderia editar sua postagem para dizer qual shell você está usando ou quais requisitos de portabilidade você possui?
Michael Homer
@MichaelHomer Claro, mas como exatamente posso verificar essas informações?
Rafael Muynarsk
É mais algo que você escolhe do que algo para verificar. Por exemplo, você está executando seus scripts com ash, ou bash, ou csh, ou dash, ..., ou zsh, ou POSIX sh, ou precisa trabalhar em diferentes sistemas?
Michael Homer
@ MichaelHomer Ah, entendo ... estou usando o bash. Vou mudar a pergunta final e inserir uma nova tag.
Rafael Muynarsk

Respostas:

21

Você pode fazer isso usando a expansão indireta da variável do bash (desde que não seja permitido deixar de fora a $variável de referência):

$ var=test
$ test="my string"
$ echo "$var"
test
$ echo "${!var}"
my string

3.5.3 Expansão dos parâmetros do shell

jesse_b
fonte
10

Para o caso em que o nome da variável contido em varé prefixado, $você pode usar eval:

$ var='$test'
$ test="my string"
$ eval echo $var
my string

O que acontece aqui:

  • bash se expande $varpara o valor $test, produzindo um eval echo $testcomando;
  • evalavalia a echo $testexpressão e produz o resultado desejado.

Observe que o uso evalem geral pode ser perigoso (dependendo do que está armazenado var), portanto, prefira evitá-lo. O recurso de expansão indireta é melhor para o seu caso (mas você precisa se livrar do $login $test).

Danila Kiver
fonte
Claro, isso é um erro de digitação. A postagem original tem aspas simples. Fixo.
Danila Kiver
4
Eu sempre recomendo referências de variáveis ​​com aspas duplas (para evitar problemas de divisão inesperada de palavras e expansão de curinga). Com eval, você precisa de duas camadas de aspas duplas, esta linha: eval echo "\"$var\"". Lembre-se, isso não faz nada sobre os outros perigos do uso evalque você mencionou.
Gordon Davisson
9

Semelhante à resposta de Jesse_b , mas usando uma variável de referência de nome em vez de variável indireta (requer bash4.3+):

$ declare -n var=test
$ test="my string"
$ echo "$var"
my string

A variável de referência de nome varcontém o nome da variável à qual está fazendo referência. Quando a variável é desreferenciada como $var, o valor dessa outra variável é retornado.

bash resolve recursivamente as referências de nome:

$ declare -n var1=var2
$ declare -n var2=test
$ test="hello world"
$ echo "$var1"
hello world

Para completar, o uso de uma matriz associativa (em bash4.0+) também é uma maneira de resolver isso, dependendo dos requisitos:

$ declare -A strings
$ strings[test]="my string"
$ var=test
$ echo "${strings[$var]}"
my string

Isso fornece uma maneira mais flexível de acessar mais de um valor por uma chave ou nome que pode ser determinado dinamicamente. Isso pode ser preferível se você deseja coletar todos os valores de uma categoria específica em uma única matriz, mas ainda conseguir acessá-los por alguma chave (por exemplo, nomes acessíveis por ID ou nomes de caminho acessíveis por finalidade etc.), pois não polui o espaço de nomes variável do script.

Kusalananda
fonte
3

Com zsh:

$ var='$test'
$ test='*'
$ printf '%s\n' ${(e)var}
*

Com qualquer concha tipo Bourne

$ eval 'printf "%s\n" "'"$var"'"'
*

Lembre-se de que nesses shells expansões variáveis ​​devem ser citadas para evitar split + glob ( zshsendo uma exceção) e echonão podem ser usadas para dados arbitrários.

Como com os dois (e)e com a evalavaliação do código do shell, é importante que o conteúdo $varfique sob seu controle, caso contrário isso pode ser uma vulnerabilidade arbitrária à injeção de comandos (o mesmo com as ${!var}abordagens).

Stéphane Chazelas
fonte