Executando verificação de segurança...
6

Uma linha de código indispensável no seu shell script

TL;DR: coloque set -euo pipefail no início do seu script e evite efeitos colaterais indesejados como a leitura de variáveis indefinidas, a não interrupção do fluxo de execução em erros e o retorno do exit code apenas do último comando dentro do pipeline.

Introdução

Shell Script é uma linguagem que adoro devido a seu fácil acesso e integração com as ferramentas e programas do sistema. No entanto, é necessário ficar atento a um detalhe importante para evitar efeitos colaterais indesejados em seus programas: diferentemente de muitas linguagens, por padrão, o Shell Script não interrompe a execução caso receba um exit code diferente de zero (erro) e também não interpreta variáveis indefinidas como erros. Por isso hoje quero lhe apresentar uma opção para ajudar a evitar problemas como esses.

O comando set

O set é um comando built-in do shell presente em sistemas GNU/Linux que configura e mostra os nomes e valores das variáveis de ambiente do shell.

A sintaxe para usa-lo é bem simples:

set [opções] [argumentos]

Existem muitas opções dísponiveis, dentre as quais você pode verificar aqui, mas hoje abordaremos somente as opções -e, -u e -o pipefail, que já são suficientes para o nosso propósito.

A opção -u

Antes de tudo, crie um arquivo chamado testando_set.sh, copie o código abaixo e dê permissão de execução através do chmod +x testando_set.sh

#!/bin/bash

parametro=$1
echo "O parâmetro passado foi: $parametro"
echo "Fim do programa"

Agora tente executa-lo no terminal assim:

./testando_set.sh

Percebeu o problema? No terminal nós não passamos o parâmetro requisitado pela variável da linha 3 e até mesmo quando tentamos imprimir essa variável na linha 4 nenhum erro foi reportado ou programa foi interrompido. Para evitar esse tipo de problema, podemos usar o set -u, que tratará o uso de toda variável não definida como um erro e automaticamente interromperá a execução.

ℹ️Para deixar um pouco mais claro o que o comando pretende fazer, também é possível utilizar o set -o nounset ao invés do set -u.

Execute o código abaixo e note que o fluxo do programa será interrompido dessa vez:

#!/bin/bash

set -u

parametro=$1
echo "O parâmetro passado foi: $parametro"
echo "Fim do programa"

A opção -e

Essa opção diz ao interpretador shell que ele deve interromper o fluxo se algum comando retornar um valor diferente de zero como status. Isso ajuda a detectar problemas antes que se tornem mais complexos.

ℹ️Para deixar um pouco mais claro o que o comando pretende fazer, também é possível utilizar o set -o errexit ao invés do set -e.

Faça o teste executando o script abaixo:

#!/bin/bash

set -e

ls "Cristóvão_Colombo.txt" # Arquivo que não existe
echo "Fim do programa" # Não será executado!

A opção -o pipefail

Por padrão, dentro de um pipeline o shell retorna o exit code do último comando, ou seja, ele poderá retornar o exit code 0 (sucesso), mesmo que todos os outros comandos dentro do pipeline retornem um exit code de erro.

Para ilustrar melhor essa situação, tente rodar o seguinte script no seu terminal:

#!/bin/sh

# Tenta imprimir o conteúdo do arquivo "Napoleão.txt" que não existe
cat Napoleão.txt
# Verifica o exit code do comando anterior
echo "exit code: $?"

# Tenta imprimir o conteúdo do arquivo "Napoleão.txt" que não existe e substituir a string "perdeu" por "ganhou" do conteúdo redirecionado pelo pipe
cat Napoleão.txt | sed 's/perdeu/ganhou/'
# Verifica o exit code do comando anterior
echo "exit code: $?"

Perceba que o primeiro exit code retornado é 1 (Operação não permitida), porém o segundo é 0 (Sucesso), mesmo o arquivo Napoleão.txt não existindo, pois o segundo comando, que possui um exit code de 1, é quem definirá o exit code do pipeline por ser o último. Para evitar esse comportamento e garantir que o pipeline retorne um exit code de sucesso apenas quando todos os comando tiverem sucesso, podemos usar o set -o pipefail.

Execute o código abaixo e perceba que agora ambos os exit codes retornados são 1, ou seja, erro.

#!/bin/sh

set -o pipefail

# Tenta imprimir o conteúdo do arquivo "Napoleão.txt" que não existe
cat Napoleão.txt
# Verifica o status code do comando anterior
echo "status code: $?"

# Tenta imprimir o conteúdo do arquivo "Napoleão.txt" que não existe e substituir a string "perdeu" por "ganhou"
cat Napoleão.txt | sed 's/perdeu/ganhou/'
# Verifica o status code do comando anterior
echo "status code: $?"

Bônus

Caso você queira fazer o contrário e garantir que determinada variável de ambiente seja desligada, você pode utilizar o sinal de + ao invés do -.

#!/bin/sh

# Ativa a checagem de erro
set -e
ls / # Lista o diretório /
echo -e "Essa mensagem será impressa, pois não possui nenhum erro até aqui\n"

# Desativa a checagem de erro
set +e
ls "Vasco_da_Gama.txt" # Esse arquivo não existe!
echo -e "Essa mensagem será impressa mesmo com o erro da linha anterior\n"

# Ativa a checagem de erro novamente
set -e
ls "Vasco_da_Gama.txt" # Esse arquivo não existe!
echo "Essa mensagem NÃO será impressa, pois existe um erro na linha anterior"

Conclusão

Pronto, agora é só juntar tudo assim set -euo pipefail e colocar no topo do seu código que já vai conseguir evitar muitos problemas bobos que poderiam se tornar complexos com o tempo.

Espero que tenha te ajudado e até a próxima! :)

Carregando publicação patrocinada...