Escopo de Variáveis e Status de Retorno

ShellBeginner
Pratique Agora

Introdução

Ao escrever scripts, você precisa controlar como os dados são acessados e como as funções comunicam seu sucesso ou falha.

Se você usar o mesmo nome de variável em dois lugares diferentes, uma variável poderá sobrescrever a outra acidentalmente. Esse é um erro comum em programação. Para evitá-lo, usamos o escopo (mantendo as variáveis locais a uma função).

Além disso, as funções precisam de uma forma de informar ao script principal se foram executadas com sucesso ou falharam. No Bash, isso é feito usando códigos de status de retorno.

Este tema reúne duas ideias que se tornam essenciais em scripts maiores: impedir que as variáveis vazem para partes do script que não deveriam ser afetadas e usar o status de sucesso ou falha para controlar o fluxo de execução.

Neste laboratório, você aprenderá:

  1. Escopo de variáveis: usar local para evitar conflitos entre variáveis.
  2. Status de retorno: usar códigos de saída (0 para sucesso e valores diferentes de zero para falha) para controlar o fluxo do script.

Escopo de Variáveis: Global e Local

Por padrão, as variáveis no Bash são globais. Isso significa que, se você definir uma variável dentro de uma função, ela poderá ser acessada e alterada em qualquer parte do script. Isso pode causar erros difíceis de identificar, quando uma parte do script altera acidentalmente dados necessários em outra parte.

Para evitar isso, usamos a palavra-chave local. Uma variável local existe somente dentro da função em que foi criada.

Essa é uma das compensações da programação em shell. O Bash facilita o início rápido, mas essa conveniência também pode facilitar o vazamento inesperado de dados pelo script. O escopo é a regra que limita onde uma variável fica visível.

Vamos demonstrar essa diferença.

Crie um script chamado scope.sh:

cd ~/project
nano scope.sh

Adicione o código a seguir:

#!/bin/bash

## This is a Global variable
city="New York"

print_city() {
  ## This is a Local variable. It shares the name 'city', but it is a separate variable.
  local city="London"
  echo "Inside function: $city"
}

echo "Before function call: $city"

## Run the function
print_city

## Check if the Global variable was changed
echo "After function call: $city"

Salve (Ctrl+O, Enter) e saia (Ctrl+X).

Torne o script executável e execute-o:

chmod +x scope.sh
./scope.sh

Resultado:

Before function call: New York
Inside function: London
After function call: New York

Observe que a variável city fora da função continuou com o valor "New York". A função usou sua própria cópia local de city ("London") e não alterou a variável global.

Se não tivéssemos usado local, a função teria sobrescrito a variável global, e a última linha teria exibido "London".

Por isso, local se torna mais importante à medida que os scripts crescem. Em uma demonstração curta, as variáveis globais podem parecer inofensivas. Em um script de manutenção mais longo, reutilizar um nome de variável pode criar erros sutis e difíceis de diagnosticar.

Códigos de Status de Retorno

Em muitas linguagens de programação, as funções retornam dados, como uma string ou um número. No Bash, return é usado estritamente para códigos de status, que indicam sucesso ou falha.

  • 0: sucesso.
  • 1-255: falha (erro).

Não confunda return com echo.

  • echo exibe texto na tela (ou em uma variável, se a saída for capturada).
  • return envia um número oculto ao sistema para informar se a função foi executada com sucesso ou falhou.

Essa distinção é fundamental na programação de shell. A saída de texto e o relatório de status são dois canais separados. Uma função pode exibir informações legíveis com echo, mas o valor de return é destinado ao controle do fluxo, não ao transporte de dados gerais.

Vamos criar uma função que simula uma falha retornando o código 5.

Crie status.sh:

nano ~/project/status.sh

Adicione o código a seguir:

#!/bin/bash

check_file() {
  echo "Checking for file..."
  ## We simulate a specific error by returning Code 5
  return 5
}

check_file

Salve e saia.

Torne o script executável e execute-o:

chmod +x status.sh
./status.sh

Saída:

Checking for file...

Você vê o texto, mas onde está o número 5? Ele foi retornado silenciosamente. Na próxima etapa, você aprenderá a capturá-lo e usá-lo.

Capturar o Código de Retorno ($?)

Para consultar o código de retorno do último comando ou função executado, o Bash fornece uma variável especial: $?.

Quando check_file terminar de ser executada, $? conterá automaticamente o valor 5.

Regra fundamental: você precisa ler $? imediatamente após a execução do comando. Assim que você executar outro comando, até mesmo um echo simples, $? será atualizado com o status desse novo comando.

Vamos modificar status.sh para capturar o código.

Abra o arquivo:

nano status.sh

Atualize o script:

#!/bin/bash

check_file() {
  echo "Checking for file..."
  return 5
}

check_file

## Capture the return code immediately into a variable
ret_val=$?

echo "The function returned: $ret_val"

Salve e saia. Execute o script novamente:

./status.sh

Saída:

Checking for file...
The function returned: 5

Agora capturamos o código de erro na variável ret_val.

Capturar o código de retorno permite que o restante do script reaja de forma inteligente. Uma instrução if posterior pode verificar esse valor e decidir se deve continuar, tentar novamente ou parar. É assim que as funções começam a colaborar em scripts maiores, em vez de agir como trechos de código isolados.

Agir com Base no Status de Retorno

Agora que temos o código de status, podemos usar uma instrução if para tratar os erros corretamente.

  • Se o código for 0, está tudo certo.
  • Se o código for 5, saberemos exatamente o que deu errado, por exemplo, "File missing".

É assim que os comandos padrão do Linux funcionam. Por exemplo, grep retorna 0 quando encontra uma correspondência e 1 quando não encontra.

Atualize status.sh para incluir essa lógica:

nano status.sh

Adicione a lógica condicional:

#!/bin/bash

check_file() {
  echo "Checking for file..."
  return 5
}

check_file
ret_val=$?

## Logic based on the return code
if [ $ret_val -eq 0 ]; then
  echo "Success: File found."
elif [ $ret_val -eq 5 ]; then
  echo "Error: File missing (Code 5)."
else
  echo "Unknown Error: Code $ret_val"
fi

Salve e saia. Execute o script:

./status.sh

Saída:

Checking for file...
Error: File missing (Code 5).

Agora você implementou um tratamento básico de erros. Se a verificação do arquivo falhar, o script saberá disso e poderá exibir uma mensagem de erro específica, em vez de falhar ou continuar sem verificar o problema.

Resumo

Neste laboratório, você praticou dois conceitos essenciais de scripting:

  1. Escopo de variáveis (local): usar local garante que as variáveis dentro das funções não sobrescrevam variáveis globais, mantendo o comportamento do script previsível.
  2. Status de retorno (return, $?): usar códigos de saída permite que as funções comuniquem sucesso ou falha ao script principal, possibilitando escrever scripts que tratam erros de forma inteligente.

Sempre use local para as variáveis dentro das funções, a menos que você precise modificar especificamente uma variável global.