No Bash, entender o escopo das variáveis é fundamental para escrever scripts robustos e previsíveis. O escopo define onde uma variável existe e pode ser acessada dentro do seu script, funções e subprocessos. Sem esse entendimento, você pode encontrar comportamentos inesperados, como variáveis que são modificadas em uma função e afetam todo o script, ou variáveis que não estão disponíveis em subprocessos quando você esperava que estivessem.

Nesta aula, vamos explorar os conceitos de variáveis globais e locais, a palavra-chave local, a diferença entre variáveis de ambiente e variáveis de shell, e o comando export. Com exemplos práticos, você verá como cada um desses conceitos se aplica no dia a dia do desenvolvimento de scripts.

Global vs local

No Bash, todas as variáveis são globais por padrão, a menos que sejam declaradas dentro de uma função com a palavra-chave local. Uma variável global é acessível de qualquer lugar do script, inclusive dentro de funções. Isso pode ser conveniente, mas também pode levar a efeitos colaterais indesejados se uma função modificar uma variável global sem intenção.

Vamos ver um exemplo simples de uma variável global:

#!/bin/bash
minha_variavel="valor global"

minha_funcao() {
    echo "Dentro da função: $minha_variavel"
}

minha_funcao
echo "Fora da função: $minha_variavel"

Ao executar, você verá que a função consegue acessar a variável global normalmente. Mas se uma função atribuir um novo valor a uma variável global, isso afetará o valor fora da função:

#!/bin/bash
minha_variavel="valor global"

minha_funcao() {
    minha_variavel="valor modificado"
}

echo "Antes: $minha_variavel"
minha_funcao
echo "Depois: $minha_variavel"

Esse comportamento pode ser útil em algumas situações, mas muitas vezes queremos que as variáveis usadas dentro de funções sejam isoladas, para evitar que uma função altere acidentalmente o estado do script. Para isso, usamos a palavra-chave local.

local

A palavra-chave local é usada dentro de funções para declarar variáveis que são locais àquela função. Isso significa que a variável só existe enquanto a função está executando e não pode ser acessada fora dela. Além disso, uma variável local não afeta nenhuma variável global com o mesmo nome.

Exemplo:

#!/bin/bash
minha_variavel="valor global"

minha_funcao() {
    local minha_variavel="valor local"
    echo "Dentro da função: $minha_variavel"
}

echo "Antes da função: $minha_variavel"
minha_funcao
echo "Depois da função: $minha_variavel"

A saída será:

Antes da função: valor global
Dentro da função: valor local
Depois da função: valor global

Note que a variável global não foi alterada pela função. Isso é extremamente útil para evitar efeitos colaterais e tornar suas funções mais previsíveis e reutilizáveis.

É importante lembrar que local só pode ser usado dentro de uma função. Se você tentar usar fora de uma função, receberá um erro de sintaxe. Boa prática: sempre declare variáveis temporárias dentro de funções como local.

Variáveis de ambiente vs shell

No Bash, há duas categorias de variáveis: variáveis de shell e variáveis de ambiente. As variáveis de shell são aquelas definidas no shell atual e estão disponíveis apenas para ele. Elas não são passadas para subprocessos (como scripts filhos ou comandos executados). Já as variáveis de ambiente são herdadas por subprocessos.

Quando você define uma variável simplesmente com nome=valor, ela é uma variável de shell. Por exemplo:

NOME="João"
echo $NOME

Essa variável existe no shell atual, mas se você executar um script filho, ele não a verá automaticamente. Para verificar se uma variável é de ambiente, você pode usar o comando env ou printenv.

Exemplo:

# Definindo uma variável de shell
MINHA_VAR="teste"
# Tentando acessar em um subprocesso
bash -c 'echo "$MINHA_VAR"'  # Saída vazia

Para que a variável seja visível em subprocessos, ela precisa ser exportada. Isso nos leva ao próximo tópico.

export

O comando export transforma uma variável de shell em uma variável de ambiente, fazendo com que ela seja passada para todos os subprocessos criados a partir do shell atual. Isso é essencial quando você quer que scripts filhos ou comandos externos tenham acesso a determinados valores.

Sintaxe:

export NOME="valor"
# ou
export NOME

Exemplo prático:

#!/bin/bash
export MEU_NOME="Maria"

# Subprocesso vê a variável
bash -c 'echo "Olá, $MEU_NOME"'

Ao executar, a saída será Olá, Maria. Sem o export, a variável não estaria disponível no subprocesso.

É possível também exportar variáveis em uma única linha ao executar um comando, usando a sintaxe:

VARIAVEL="valor" comando

Isso define a variável apenas para a execução do comando, sem afetar o shell atual. Por exemplo:

LANG="pt_BR.UTF-8" locale

Isso é útil para modificar o ambiente de um comando específico.

Para listar todas as variáveis de ambiente, use env. Para listar todas as variáveis de shell, use set.

Boas práticas

Ao trabalhar com variáveis no Bash, siga estas boas práticas:

  • Sempre declare variáveis locais dentro de funções usando local para evitar efeitos colaterais.
  • Use nomes de variáveis em maiúsculas para variáveis de ambiente e em minúsculas para variáveis locais, seguindo convenções comuns.
  • Use export somente quando a variável precisar ser acessada por subprocessos; evite exportar desnecessariamente para não poluir o ambiente.
  • Prefira usar local em funções para variáveis temporárias, mesmo que você não preveja conflitos, pois isso torna o código mais seguro.
  • Para verificar se uma variável está definida, use [[ -v nome_variavel ]] ou ${nome_variavel+x}.

Referências

Exercícios

  1. Escreva um script que define uma variável global, depois uma função que tenta modificar essa variável usando local com o mesmo nome, e mostre que a variável global permanece inalterada.
  2. Explique a diferença entre variáveis de shell e variáveis de ambiente, e dê um exemplo de como verificar se uma variável é de ambiente.
  3. Crie um script que exporte uma variável e chame um subprocesso (por exemplo, bash -c) para exibir o valor, demonstrando que o subprocesso a enxerga.
  4. O que acontece se você usar local fora de uma função? Teste e explique.
  5. Qual é a saída do seguinte script e por quê?
#!/bin/bash
var="global"
func() {
    local var="local"
    echo "Dentro: $var"
}
func
echo "Fora: $var"

✓ Resposta:
#!/bin/bash
var="global"
func() {
    local var="local"
    echo "Dentro: $var"
}
func
echo "Fora: $var"
O script mostra que a variável global permanece global após a função, pois local cria uma variável com escopo restrito à função.

✓ Resposta: Variáveis de shell são definidas apenas no shell atual e não são herdadas por subprocessos. Variáveis de ambiente são herdadas por subprocessos. Para verificar se uma variável é de ambiente, use env | grep NOME ou printenv NOME. Exemplo: export TESTE=1; printenv TESTE mostra o valor, enquanto TESTE=1; printenv TESTE não mostra nada.

✓ Resposta:
#!/bin/bash
export NOME="Maria"
bash -c 'echo "Olá, $NOME"'
O subprocesso exibe Olá, Maria porque a variável foi exportada.

✓ Resposta: Se você usar local fora de uma função, o Bash retorna um erro de sintaxe: local: can only be used in a function. O script para a execução.

✓ Resposta: A saída é:
Dentro: local
Fora: global
Dentro da função, local var define uma variável local com valor local, que é exibida. Fora, a variável global global permanece.