Aula 5 – Variáveis Especiais

Aula 4 – Variáveis | Índice | Aula 6 – Vetores


Seu apoio é muito importante para a criação e a manutenção dos cursos gratuitos do canal debxp:


5.1 – Caracteres com significado especial

Uma coisa interessante sobre o shell/bash, é que ele utiliza alguns caracteres simples para representar os nomes de algumas variáveis que ele mesmo define durante uma sessão.

Por exemplo, o caractere $, além de ser o prefixo indicador de acesso a uma variável, também é o nome da variável em que o shell armazena o PID da sua própria sessão corrente. Ou seja, se executarmos…

:~$ echo $$

O retorno será o PID da sessão corrente do shell.

Aliás, esta é uma ótima oportunidade para comprovarmos o fato de um script em execução ser uma sessão do shell diferente daquela em que ele é chamado!

Crie este script com o nome teste_pid:

#!/usr/bin/env bash
echo "- O meu PID é: $$"
exit 0

Agora, execute-o no terminal desta forma (não se esqueça do chmod +x ...):

:~$ ./teste_pid; echo "- O PID desta sessão do shell é: $$"

Aqui, o resultado será parecido com isso:

:~$ ./teste_pid; echo "- O PID desta sessão do shell é: $$"
- O meu PID é: 31793
- O PID desta sessão do shell é: 15647
:~$

5.2 – Obtendo o status de saída do último comando

Outra variável especial muito útil é a $?. Com ela, nós podemos verificar o status de saída do último comando executado no terminal ou a partir de um script. Se o comando tiver sido executado com sucesso, o retorno será 0 (zero). Caso contrário, a variável $? armazenará qualquer valor diferente de zero.

Por exemplo…

:~$ apt install banana
# mensagem de erro #
:~$ echo $?
100

:~$ echo "Olá mundo"
Olá mundo
:~$ echo $?
0

O primeiro comando encerrou com erro 100 (e não usei sudo e o pacote banana não existe). Já o segundo comando encerrou com sucesso, portanto o seu status de saída foi zero. Isso é muito útil, especialmente dentro de scripts, para determinar o sucesso da execução de algum comando e, a partir dessa informação, tomar uma decisão.

Por exemplo, na primeira aula nós vimos que o comando interno help retorna um erro caso o comando de que buscamos informações não seja um comando interno, e agora nós podemos criar um script justamente para esta finalidade!

Vamos chamar o nosso script de checa_builtin e, para começar, vamos digitar os seguintes comandos nele:

#!/usr/bin/env bash

# Aqui nós executamos o comando "help"...
help ls &> /dev/null

# E aqui nós testamos a saída com o comando "test"...
[[ $? -eq 0 ]] && echo "ls é interno!" || echo "ls não é interno!"

exit 0

Antes de começarmos a análise deste código, você deve saber que as linhas que iniciam com cerquilha (#) são chamadas de "comentários" e elas são ignoradas pelo shell. Elas existem justamente para que o programador faça comentários no código, o que serve tanto para que ele se lembre do que pretendia fazer com determinado comando, quanto para facilitar a compreensão do código quando ele for lido por outras pessoas.

Vamos, então, ao primeiro comando:

help ls &> /dev/null

Aqui nós pedimos ao help que nos mostre as informações do comando ls. Como não estamos interessados na descrição, e sim se o comando help vai sair com erro, nós vamos redirecionar a saída de tudo que apareceria no terminal (um erro ou a informação sobre o comando ls) para o arquivo /dev/null.

De forma bem simplificada, o arquivo /dev/null pode ser entendido como o "limbo" do shell, um local para onde podemos mandar qualquer coisa que nós queremos que desapareça.

O redirecionamento da saída é feito com o operador > (que nós já vimos quando falamos da criação de arquivos vazios) acrescido do símbolo &, indicando que queremos redirecionar tanto as mensagens normais que seriam exibidas no terminal (saída padrão, ou STDOUT) quanto as mensagens de erro (saída padrão de erro, ou STDERR).

Não se preocupe, nós teremos uma aula inteira sobre redirecionamentos no módulo avançado, quando falarmos dos "descritores de arquivos". No momento, basta saber que…

&> /dev/null

Impedirá que qualquer mensagem retornada pelo comando "help" seja exibida no terminal. Porém, mesmo sem exibir mensagem nenhuma, o sucesso ou o erro de execução do comando help ficará armazenado na variável $?, o que nos leva à próxima linha do script, onde nós utilizamos o comando interno test.

[[ $? -eq 0 ]]

O comando test avalia expressões condicionais, ou seja, ele retorna sucesso (0) caso uma expressão seja verdadeira, ou erro (1) se a expressão for falsa.

A expressão, no nosso caso, é uma comparação. Estamos comparando o valor em $? com o valor numérico 0 (zero), por isso utilizamos o operador de comparação numérica -eq (de equal, ou igual).

O comando test é um dos builtins mais interessantes e poderosos do shell/bash! Com ele podemos testar uma grande infinidade de coisas, da existência de arquivos e pastas a condições lógicas. Vale muito a pena conferir nos manuais o que ele é capaz de fazer executando: help test e help [[.

O comando test pode ser executado das seguintes formas:

[[ $? -eq 0 ]]

Ou então…

test $? -eq 0

Mas, a primeira forma, além de ter alguns recursos a mais, só funciona no Bash.

Ao lado do teste, nós utilizamos os operadores de concatenação condicional && (AND/E) e || (OR/OU).

Sua principal função é executar ou não o comando seguinte de acordo com o status de saída do comando que os preceder. Se o comando antes de && retornar um status 0 (sucesso), o comando seguinte será executado. Caso contrário, o comando a ser executado será aquele que vier depois do operador ||:

comando1 && comando_sucesso || comando_erro

Por isso, no script checa_builtin, nós utilizamos…

[[ $? -eq 0 ]] && echo "ls é interno!" || echo "ls não é interno!"

Ou seja, se o comando test retornar "sucesso" (se $? for mesmo igual a zero), o comando a ser executado será…

echo "ls é interno!"

Caso retorne "erro", o comando executado será…

echo "ls não é interno!"

Entendido o funcionamento do script, vamos executá-lo.

:~$ ./checa_builtin
ls não é interno!

O que era de se esperar.

5.3 – Passando argumentos para o scripts

Mas seria muito chato ter que editar o script toda vez que quiséssemos testar se um comando é ou não é interno! E se fosse possível executar o script checa_builtin informando, na própria linha de comando, o comando que queremos testar?

Pois bem, isso é possível, graças à variável especial $n, onde n é um número inteiro a partir de 0.

Nós estamos utilizando o termo "argumentos" por uma questão de similaridade com outras linguagens, mas o termo correto para isso no Bash é parâmetros (ou "valores"). É por este motivo que, tecnicamente, essas variáveis especiais são chamadas de parâmatros posicionais.

Aliás, nós utilizamos essa variável especial na primeira aula, quando queríamos descobrir qual era o shell em execução, lembra?

:~$ echo $0
/bin/bash

Na ocasião, nós vimos que $0 armazena o nome do programa/script em execução (no caso, era o bash). Os demais números, começando do 1, irão representar, em ordem de ocorrência, os argumentos passados para o script. Desta forma:

:~$ script argumento_1 argumento_2 ... argumento_N

$0 => "script"
$1 => "argumento_1"
$2 => "argumento_2"
...
$n => "argumento_N"

Então, de volta ao script checa_builtin, nós podemos combinar que ele será executado assim:

:~$ ./checa_builtin comando_a_ser_testado

O próprio shell cuidará de armazenar comando_a_ser_testado na variável especial $1, e nós poderemos utilizá-la dentro do nosso script.

Então, vamos às alterações!

Para isso, nós vamos substituir todas as ocorrências de ls por $1

#!/usr/bin/env bash

# Aqui nós executamos o comando "help"...
help $1 &> /dev/null

# E aqui nós testamos a saída com o comando "test"...
[[ $? -eq 0 ]] && echo "$1 é interno!" || echo "$1 não é interno!"

exit 0

Vamos ver o que acontece quando testamos os comandos (será?) cat e cd?

:~$ ./checa_builtin cat
cat não é interno!

:~$ ./checa_builtin cd
cd é interno!

5.4 – Contando o número de argumentos

Nós sabemos que o nosso script depende do argumento comando_a_ser_testado para funcionar corretamente. Mas, e se ele cair nas mãos de um usuário desavisado?

Por isso, nós precisamos testar, no mínimo, se a quantidade de argumentos passados na execução do script corresponde ao número de argumentos que nós esperamos (no caso, apenas um).

Felizmente, o shell também armazena essa informação na variável especial $#.

No nosso caso, a quantidade esperada é de apenas um argumento, e nós podemos utilizar novamente o comando test para verificar se essa quantidade bate…

[[ $# -ne 1 ]] && mensagem_de_erro && exit 1

Nesse trecho, nós utilizamos o operador de comparação numérica -ne (de not equal, ou "diferente"). Ou seja, se a quantidade de argumentos ($#) for diferente de 1, uma mensagem de erro deve ser enviada e, em seguida, o script deve ser interrompido com o status de saída 1 (indicando um erro).

Então, vamos alterar o script. Mas, para que as linhas não fiquem longas demais, eu vou armazenar a mensagem de erro numa variável chamada msg

#!/usr/bin/env bash

# Mensagem de erro...
msg="É preciso informar um comando válido!"

# Teste de quantidade de argumentos...
[[ $# -ne 1 ]] && echo $msg && exit 1

# Aqui nós executamos o comando "help"...
help $1 &> /dev/null

# E aqui nós testamos a saída com o comando "test"...
[[ $? -eq 0 ]] && echo "$1 é interno!" || echo "$1 não é interno!"

exit 0

Fazendo alguns testes…

:~/scripts$ ./checa_builtin
É preciso informar um comando válido!

:~/scripts$ ./checa_builtin cat
cat não é interno!

:~/scripts$ ./checa_builtin test
test é interno!

5.5 – Um pequeno resumo

Ainda existem algumas outras variáveis especiais, mas vamos com calma, porque você já tem novidades demais para assimilar. Então, por enquanto, vamos apenas relembrar as variáveis do shell que nós vimos até aqui.

Variável Descrição
$? Armazena o status de saída do último comando executado.
$$ Armazena o PID da sessão corrente do shell.
$0 Armazena o nome do arquivo do programa ou do script sendo executado.
$n (a partir de 1) Armazena os valores (parâmetros) passados para o script, onde "n" é um número inteiro, positivo, correspondente à posição dos parâmetros na linha de comando (daí "posicionais").
$# Armazena o número de parâmetros passados para o script.

Aula 4 – Variáveis | Índice | Aula 6 – Vetores

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Post comment