Como usar eval em scripts Bash do Linux

Publicados: 2022-08-22
Laptop Linux mostrando um prompt bash
fatmawati achmad zaenuri/Shutterstock.com

De todos os comandos do Bash, o pobre e velho eval provavelmente tem a pior reputação. Justificado, ou apenas má imprensa? Discutimos o uso e os perigos desse menos amado dos comandos do Linux.

Precisamos Falar Sobre Aval

Usado de forma descuidada, o eval pode levar a um comportamento imprevisível e até mesmo a inseguranças do sistema. Pelos sons, provavelmente não deveríamos usá-lo, certo? Bem, não exatamente.

Você poderia dizer algo semelhante sobre automóveis. Nas mãos erradas, eles são uma arma mortal. As pessoas os usam em ataques de aríete e como veículos de fuga. Devemos todos parar de usar carros? Não, claro que não. Mas eles têm que ser usados ​​corretamente e por pessoas que sabem como conduzi-los.

Como trabalhar com variáveis ​​no Bash
RELACIONADO Como trabalhar com variáveis ​​no Bash

O adjetivo usual aplicado a eval é “mal”. Mas tudo se resume a como ele está sendo usado. O comando eval agrupa os valores de uma ou mais variáveis. Ele cria uma string de comando. Em seguida, ele executa esse comando. Isso o torna útil quando você precisa lidar com situações em que o conteúdo de um comando é derivado dinamicamente durante a execução de seu script.

Os problemas surgem quando um script é escrito para usar eval em uma string que foi recebida de algum lugar fora do script. Ele pode ser digitado por um usuário, enviado por meio de uma API, marcado em uma solicitação HTTPS ou em qualquer outro lugar externo ao script.

Se a string na qual eval vai funcionar não foi derivada local e programaticamente, existe o risco de que a string possa conter instruções maliciosas incorporadas ou outra entrada mal formada. Obviamente, você não quer que o eval execute comandos maliciosos. Portanto, para estar seguro, não use eval com strings geradas externamente ou entrada do usuário.

Primeiros passos com eval

O comando eval é um comando interno do shell Bash. Se Bash estiver presente, eval estará presente.

eval concatena seus parâmetros em uma única string. Ele usará um único espaço para separar elementos concatenados. Ele avalia os argumentos e, em seguida, passa a string inteira para o shell executar.

Vamos criar uma variável chamada wordcount .

 wordcount="wc -w raw-notes.md"

A variável string contém um comando para contar as palavras em um arquivo chamado “raw-notes.md”.

Podemos usar eval para executar esse comando passando o valor da variável.

 eval " $wordcount "

Usando eval com uma variável de string para contar as palavras em um arquivo

O comando é executado no shell atual, não em um subshell. Podemos mostrar isso facilmente. Temos um pequeno arquivo de texto chamado “variables.txt”. Ele contém essas duas linhas.

 primeiro=Como fazer
segundo=Geek

Usaremos cat para enviar essas linhas para a janela do terminal. Em seguida, usaremos eval para avaliar um comando cat para que as instruções dentro do arquivo de texto sejam executadas. Isso definirá as variáveis ​​para nós.

 cat variáveis.txt
eval "$(cat variables.txt)"
echo $primeiro $segundo 

Acessando variáveis ​​definidas por eval no shell atual

Usando echo para imprimir os valores das variáveis, podemos ver que o comando eval é executado no shell atual, não em um subshell.

Um processo em um subshell não pode alterar o ambiente de shell do pai. Como eval é executado no shell atual, as variáveis ​​definidas por eval podem ser usadas no shell que iniciou o comando eval .

Observe que se você usar eval em um script, o shell que seria alterado por eval é o subshell no qual o script está sendo executado, não o shell que o iniciou.

RELACIONADO: Como usar os comandos cat e tac do Linux

Usando variáveis ​​na string de comando

Podemos incluir outras variáveis ​​nas strings de comando. Vamos definir duas variáveis ​​para armazenar números inteiros.

 num1=10 
num2=7

Criaremos uma variável para armazenar um comando expr que retornará a soma de dois números. Isso significa que precisamos acessar os valores das duas variáveis ​​inteiras no comando. Observe os backticks em torno da instrução expr .

 add="`expr $num1 + $num2`"

Criaremos outro comando para nos mostrar o resultado da instrução expr .

 show="eco"

Observe que não precisamos incluir um espaço no final da string echo , nem no início da string expr . eval cuida disso.

E para executar todo o comando usamos:

 eval $show $add 

Usando variáveis ​​na string de comando

Os valores das variáveis ​​dentro da string expr são substituídos na string por eval , antes de ser passado para o shell para ser executado.

RELACIONADO: Como trabalhar com variáveis ​​no Bash

Acessando variáveis ​​dentro de variáveis

Você pode atribuir um valor a uma variável e, em seguida, atribuir o nome dessa variável a outra variável. Usando eval , você pode acessar o valor mantido na primeira variável, a partir de seu nome que é o valor armazenado na segunda variável. Um exemplo irá ajudá-lo a desembaraçar isso.

Copie este script para um editor e salve-o como um arquivo chamado “assign.sh”.

 #!/bin/bash

title="Como Fazer Geek"
página da web=título
comando="eco"
eval $command \${$webpage}

Precisamos torná-lo executável com o comando chmod .

 chmod +x assign.sh 

Usando chmod para tornar um script executável

Você precisará fazer isso para todos os scripts copiados deste artigo. Basta usar o nome de script apropriado em cada caso.

Quando executamos nosso script, vemos o texto da variável title mesmo que o comando eval esteja usando a variável webpage .

 ./assign.sh 

Acessando o valor de uma variável a partir de seu nome armazenado em outra variável

O cifrão de escape “ $ ” e as chaves “ {} ” fazem com que eval veja o valor contido na variável cujo nome está armazenado na variável da webpage da web.

Usando variáveis ​​criadas dinamicamente

Podemos usar eval para criar variáveis ​​dinamicamente. Esse script é chamado de “loop.sh”.

 #!/bin/bash

total=0
label="Loop completo. Total:"

para n em {1..10}
Faz
  valor x$n=$n
  echo "Loop" $x$n
  ((total+=$x$n))
feito

eco $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10

echo $label $total

Ele cria uma variável chamada total que contém a soma dos valores das variáveis ​​que criamos. Em seguida, ele cria uma variável de string chamada label . Esta é uma seqüência simples de texto.

Vamos fazer um loop 10 vezes e criar 10 variáveis ​​chamadas x1 até x10 . A instrução eval no corpo do loop fornece o “x” e recebe o valor do contador de loop $n para criar o nome da variável. Ao mesmo tempo, ele define a nova variável para o valor do contador de loop $n .

9 Exemplos de loops em scripts Bash do Linux
RELACIONADOS 9 Exemplos de loops for em scripts Bash do Linux

Ele imprime a nova variável na janela do terminal e então incrementa a variável total com o valor da nova variável.

Fora do loop, as 10 novas variáveis ​​são impressas mais uma vez, todas em uma linha. Observe que também podemos nos referir às variáveis ​​por seus nomes reais, sem usar uma versão calculada ou derivada de seus nomes.

Por fim, imprimimos o valor da variável total .

 ./loop.sh 

Usando eval para criar variáveis ​​dinamicamente

RELACIONADO: Primer: Bash Loops: for, while e until

Usando eval com arrays

Imagine um cenário em que você tem um script de longa duração e executando algum processamento para você. Ele grava em um arquivo de log com um nome criado a partir de um carimbo de data/hora. Ocasionalmente, ele iniciará um novo arquivo de log. Quando o script termina, se não houver erros, ele exclui os arquivos de log que criou.

Você não quer que ele simplesmente rm *.log , você só quer que ele exclua os arquivos de log que ele criou. Este script simula essa funcionalidade. Este é “clear-logs.sh”.

 #!/bin/bash

declare -a arquivos de log

contagem de arquivos=0 
rm_string="eco"

function create_logfile() {
  ((++contagem de arquivos))
  nome do arquivo=$(data +"%Y-%m-%d_%H-%M-%S").log
  logfiles[$filecount]=$filename
  echo $filecount "Criado" ${logfiles[$filecount]}
}

# corpo do script. Algum processamento é feito aqui que
# gera periodicamente um arquivo de log. Vamos simular isso
create_logfile
dormir 3
create_logfile
dormir 3
create_logfile
dormir 3
create_logfile

# existem arquivos para remover?
for ((arquivo=1; arquivo<=$arquivoconta; arquivo++))
Faz
  # remove o arquivo de log
  eval $rm_string ${logfiles[$file]} "excluído..."
  arquivos de log[$file]=""
feito

O script declara um array chamado logfiles . Isso conterá os nomes dos arquivos de log criados pelo script. Ele declara uma variável chamada filecount . Isso conterá o número de arquivos de log que foram criados.

Ele também declara uma string chamada rm_string . Em um script do mundo real, isso conteria o comando rm , mas estamos usando echo para que possamos demonstrar o princípio de maneira não destrutiva.

A função create_logfile() é onde cada arquivo de log é nomeado e onde ele seria aberto. Estamos apenas criando o nome do arquivo e fingindo que ele foi criado no sistema de arquivos.

A função incrementa a variável filecount . Seu valor inicial é zero, então o primeiro nome de arquivo que criamos é armazenado na posição um na matriz. Isso é feito de propósito, como veremos mais adiante.

O nome do arquivo é criado usando o comando date e a extensão “.log”. O nome é armazenado na matriz na posição indicada por filecount . O nome é impresso na janela do terminal. Em um script do mundo real, você também criaria o arquivo real.

Como pausar um script Bash com o comando de suspensão do Linux
RELACIONADO Como pausar um script Bash com o comando de suspensão do Linux

O corpo do script é simulado usando o comando sleep . Ele cria o primeiro arquivo de log, aguarda três segundos e cria outro. Ele cria quatro arquivos de log, espaçados para que os carimbos de data e hora em seus nomes de arquivo sejam diferentes.

Por fim, há um loop que exclui os arquivos de log. O arquivo do contador de loops é definido como um. Ele conta até e incluindo o valor de filecount , que contém o número de arquivos que foram criados.

Se filecount ainda estiver definido como zero - porque nenhum arquivo de log foi criado - o corpo do loop nunca será executado porque um não é menor ou igual a zero. É por isso que a variável filecount foi definida como zero quando foi declarada e porque foi incrementada antes da criação do primeiro arquivo.

Dentro do loop, usamos eval com nossa rm_string não destrutiva e o nome do arquivo que é recuperado do array. Em seguida, definimos o elemento do array como uma string vazia.

Isso é o que vemos quando executamos o script.

 ./clear-logs.sh 

Excluindo arquivos cujos nomes estão armazenados em uma matriz

Nem tudo é ruim

O eval muito difamado definitivamente tem seus usos. Como a maioria das ferramentas, usada de forma imprudente, é perigosa e em mais de uma maneira.

Se você se certificar de que as strings nas quais ele funciona são criadas internamente e não capturadas de humanos, APIs ou coisas como solicitações HTTPS, você evitará as principais armadilhas.

RELACIONADO: Como exibir a data e hora no terminal Linux (e usá-lo em scripts Bash)