CI066 - Oficina de Programação
Notas de Aula # 12

Prof. Armando Luiz N. Delgado

1 Programação Shell - leitura de dados, variáveis de ambiente.

1.1 Leitura de Dados

Como visto na aula anterior, o comando interno read de bash(1) é usado para ler strings do teclado. A forma básica para read é:

      read [-t timeout] [-p prompt] [-s] [var1] [var2] ...

onde:

-p prompt
mostra prompt em STDERR sem mudar linha, antes de efetivamente efetuar a leitura;
-t timeout
se decorridos timeout segundos não há entrada de dados para read, este termina com falha (i.e., status não é 0 (zero));
-s
caracteres não são ecoados ao serem digitados (silent mode).

Dos dados digitados, cada palavra é assinalada como valor de cada Variável de Ambiente especificada (i.e., var1, var2, etc.). Se não são fornecidas variáveis, o valor é assinalado à variável REPLY.

ci066@dupond:~read -p "Indique Nome, Idade e Endereco: " NOME IDADE END
Indique Nome, Idade e Endereco: Armando 18 Rua dos Alfeneiros, 25
ci066@dupond:~echo $NOME
Armando
ci066@dupond:~echo $IDADE
18
ci066@dupond:~echo $END
Rua dos Alfeneiros, 25
ci066@dupond:~ 

Mais detalhes sobre o comando interno read podem ser vistos na seção SHELL BUILTIN COMMANDS do manual on-line de bash(1).

1.1.1 Lendo dados de arquivos

O comando read pode também ser também usado par ler de arquivos, redirecionando-se STDIN ou usando-se pipeline.

        read FONE < fones.txt

OBS.: O uso de redirecionamento de STDIN com read não funciona em versões de UNIX anteriores a System V Release 2.

O exemplo acima lê apenas 1 (uma) linha do arquivo fones.txt. caso se queira ler várias linhas em seqüência e efetuar alguma operação sobre cada uma, o exemplo acima não funciona. A solução é mostrada abaixo:

        cat fones.txt |
        while read ; do
           echo $REPLY
        done

Neste caso, o nome do arquivo de onde se lê está evidente no início do pipeline, evitando confusões.

NOTA: Pode-se alterar a solução acima para usar redirecionamento, embora não seja recomendado, pois o nome do arquivo fica pouco evidente no código do script:

        while read ; do
           echo $REPLY
        done < fones.txt

1.2 Variáveis Locais e Exportadas

Suponha o script varecho, que apenas mostre o valor de uma variável de ambiente x:

ci066@dupond:~cat varecho
echo ''VAR = :$x:''
ci066@dupond:~x=100
ci066@dupond:~varecho
VAR = ::
ci066@dupond:~ 

Como se vê acima, varecho não conhecia o valor de x. Assim, seu valor na execução do script é nulo e nada é mostrado. Dizemos que a variável x que foi definida no shell de login é uma variável local. Da mesma forma, a variável x definida no script é local em relação à execução do script.

Outro exemplo:

ci066@dupond:~cat varecho2
x=50
echo ''VAR = :$x:''
ci066@dupond:~x=100
ci066@dupond:~varecho2
VAR = :50:
ci066@dupond:~echo ''$x''
100
ci066@dupond:~ 

Note-se que o script varecho2 não mudou o valor de x no shell de login.

Este comportamento é devido ao fato que scripts shell são executados como subshells (processos diferentes). Toda vez que um subshell é iniciado, ele é executado em um ambiente totalmente novo, com seu próprio conjunto de variáveis de ambiente. Um subshell não tem conhecimento de variáveis locais que foram definidas pelo subshell pai (Figura 1).

Figura 1: Variáveis locais e subshells
\includegraphics[scale=0.8]{localvars}

Quando um script termina, o subshell também termina, juntamente com quaisquer variáveis definidas.

A forma usada para tornar o valor de uma variável conhecida a um subshell é exportando seu valor através do comando interno de bash(1), export:

       z="Arnoldo"
       x=100
       export x z

       export y=200

Observe o exemplo abaixo:

ci066@dupond:~cat varecho3
echo ''Y = :$y:''
echo ''X = :$x:''
ci066@dupond:~x=100
ci066@dupond:~y=10
ci066@dupond:~varecho3
Y = ::
X = ::
ci066@dupond:~export y
ci066@dupond:~varecho3
Y = :10:
X = ::

Sempre que um subshell é iniciado, uma cópia da lista de variáveis exportadas é passada ao subshell, enquanto a lista de variáveis locais não o é (Figura 2).

Figura 2: Variáveis locais e exportadas
\includegraphics[scale=0.8]{exportvars}

O que acontece se o subshell muda o valor de uma variável exportada? A mudança vai ser refletida no processo pai? A resposta à última pergunta é NÃO. Não há maneira de mudar o valor de uma variável em um shell pai a partir de um subshell:

ci066@dupond:~cat varecho4
x=50
y=5
ci066@dupond:~x=100
ci066@dupond:~y=10
ci066@dupond:~export y
ci066@dupond:~varecho4
ci066@dupond:~echo ''$x $y''
100 10
ci066@dupond:~ 

A variável y criada em varecho4 não se confunde com a variável y exportada pelo shell de login (Figura 3).

Figura 3: Mudança de valor em exportadas
\includegraphics[scale=0.8]{chexportvars}

Por outro lado, toda variável que é exportada, ele permanece exportada para todos os subshells subsequentes (Figura 4).

ci066@dupond:~cat varecho4
x=50
y=5
z=1
export z
varecho5
ci066@dupond:~cat varecho5
echo ''x = $x''
echo ''y = $y''
echo ''z = $z''
ci066@dupond:~x=100
ci066@dupond:~y=10
ci066@dupond:~export y
ci066@dupond:~varecho4
x =
y = 5
z = 1
ci066@dupond:~ 

Figura 4: Exportando variáveis através de subshells
\includegraphics[scale=0.8]{subexportvars}

Finalmente, para que as mudanças em variáveis feitas em um script tenham efeito no shell (ou subshell) corrente, usa-se o comando interno de bash(1) . ou source.

ci066@dupond:~cat vars
LIVRO=''/home/prof/delga/livro''
DOCS=''/home/prof/delga/documentos''
CAL=''/home/prof/delga/calendar''
ci066@dupond:~vars
 
ci066@dupond:~ 
ci066@dupond:~source vars
ci066@dupond:~echo $LIVRO
/home/prof/delga/livro
ci066@dupond:~ 

SUMÁRIO

  1. Qualquer variável que não é exportada é uma variável local cuja existência não será conhecida por subshells;
  2. Variáveis exportadas e seus valores são copiados para o ambiente de um subshell onde eles podem ser acessados e alterados. Contudo, tais mudanças não se refletem nas variáveis do shell pai (Figura 3);
  3. Variáveis exportadas retêm esta característica não apenas para um subshell direto, mas para todos os subshells iniciados a partir daquele (e assim por diante) (Figura 4);
  4. Uma variável pode ser exportada em qualquer momento antes ou depois de ter seu valor alterado ou definido;
  5. Para que as mudanças em variáveis feitas em um script tenham efeito no shell (ou subshell) corrente, usa-se o comando interno de bash(1) . ou source.

1.3 Controle de fluxo e iteração

Seleção case
 
case expressao in
   padrao_1a [ | padrao_1b ... ]) lista_1 ;;
   padrao_2a [ | padrao_2b ... ]) lista_2 ;;
   padrao_3a [ | padrao_3b ... ]) lista_3 ;;
   padrao_4a [ | padrao_4b ... ]) lista_4 ;;
   ...
   [ *) lista_default  ;; ]
esac

case palavra in pattern [ | pattern ] ... ) lista ;; ] ... esac
Se o valor da expressão for igual a padrão_1a OU padrão_1b, executa lista_1. Senão, se o valor da expressão for igual a padrão_2a OU padrão_2b, executa lista_2, e assim sucessivamente. O padrão * representa a execução default. É equivalente a um else. Após a execução de qualquer lista, o comando seguinte ao bloco case...esac é executado.

break
termina a execução de um laço, executando o primeiro comando APÓS o bloco que define o laço.
continue
interrompe uma iteração de um laço, passando para a próxima iteração.

2 Exemplos

Exemplos podem ser encontrados no link de Exemplos na página HTML da disciplina.



Armando Luiz Nicolini Delgado
2008-07-11