Hugo Cisneiros (Eitch), hugo arroba devin ponto com ponto br
Versão 4.0, 2006

The Linux Manual

7.1. Programação em Shell-Script

Índice

7.1.1. Primeiros passos
7.1.2. Conceito de variáveis em shell-script
7.1.3. Controles de Fluxo
7.1.4. Usando vários scripts em um só
7.1.5. Variáveis especiais
7.1.6. Funções
7.1.7. Exemplo final de shell-script

Como você já deve saber, a shell do Linux é uma poderosa ferramenta que traz ao usuário toda a flexibilidade dos sistemas Unix e todos seus comandos. No nosso caso, usamos o bash em todo o procedimento desse manual, então nada mais justo do que explicarmos um pouco de desenvolvimento com scripts-shell do bash :)

Muita gente não sabe que o bash tem uma poderosa linguagem de script embutida nele. Diversas pessoas utilizam-se desta linguagem para facilitar a realização de inúmeras tarefas administrativas em um sistema Linux, ou até mesmo para criar seus próprios programas! Para quem gosta de usar a criatividade para criar coisas e facilitar o uso do sistema no dia-a-dia, o shell-script é um prato cheio para começar.

Costumo indicar para quem está começando programação à dar uma olhada em shell-script. E faço isso porque o shell-script é bastante simples, te dá uma boa noção do básico da lógica de programação e te dá resultados imediatos! Combinando todos os comandos avançados do Linux, você consegue fazer coisas bem úteis e aprender muito! :)

Você poderá criar scripts para automatizar as tarefas diárias de um servidor, para efetuar backup automático regularmente, procurar textos, criar formatações e muito mais!

7.1.1. Primeiros passos

Uma das vantagens do shell-script é que ele não precisa ser compilado, ou seja, basta apenas criar um arquivo texto qualquer e inserir comandos nele. Para dar a este arquivo a definição de shell-script, teremos que incluir uma linha no começo do arquivo:

#!/bin/bash

Essa linha diz ao interpretador de comandos para que quando o usuário executar o arquivo, ele execute através do programa /bin/bash, significando então que é um script shell! :)

Depois de colocada a linha, basta apenas tornar o arquivo executável, utilizando o comando chmod. Vamos seguir com um pequeno exemplo de um script shell que mostre na tela: "Nossa, estou vivo!":

#!/bin/bash
echo 'Nossa! Estou vivo! Levantei da tumba! Yikes!'

Exemplo 7.1. Exemplo de script shell "Nossa, estou vivo!"

Fácil, hein? A primeira linha a gente já sabe pra que serve e a segunda linha mostrará na tela a frase "Nossa! Estou vivo!", utilizando o comando echo, que serve justamente para isso (mostrar coisas na tela). Como você pôde ver, todos os comandos que você digita diretamente na linha de comando, você poderá incluir no seu script shell, criando uma série de comandos e é essa combinação de comandos que forma o chamado shell script. Tente também dar o comando file arquivo e veja que a definição dele é de Bourne-Again Shell Script (Bash Script).

Para o arquivo poder se executável, você tem de atribuir o comando de executável para ele. E como citamos anteriormente, o comando chmod se encarrega disto:

$ chmod +x arquivo

Pronto, o arquivo poderá ser executado com um simples ./arquivo.

7.1.2. Conceito de variáveis em shell-script

Variáveis são caracteres que armazenam dados, uma espécie de atalho. O bash reconhece uma variável quando ela começa com $, ou seja, a diferença entre palavra e $palavra é que a primeira é uma palavra qualquer e a outra uma variável. Para definir uma variável, utilizamos a seguinte sintaxe:

variavel="valor"

O "valor" será atribuído à "variável". "Valor" pode ser uma frase, números e até outras variáveis e comandos. O valor pode ser expressado entre as aspas (""), apóstrofos ('') ou crases (``). As aspas vão interpretar as variáveis que estiverem dentro do valor, os apóstrofos lerão o valor literalmente, sem interpretar nada e as crases vão interpretar um comando e retornar a sua saída para a variável. Vejamos exemplos para entender melhor:

$ variavel="Eu estou logado como usuário $user"
$ echo $variavel

Eu estou logado como usuário cla

$ variavel='Eu estou logado como usuário $user'
$ echo $variavel

Eu estou logado como usuário $user

$ variavel="Meu diretório atual é o `pwd`"
$ echo $variavel

Meu diretório atual é o /home/cla

Se você quiser criar um script em que o usuário deve interagir com ele, é possível que você queira que o próprio usuário defina uma variável e para isso usamos o comando read, que dará uma pausa no script e ficarará esperando o usuário digitar algum valor e teclar Enter. Exemplo:

$ echo "Entre com o valor para a variável: " ; read variavel

O usuário digita e tecla Enter, vamos supor que ele digitou a frase "o batima eh um frutinha":

$ echo $variavel

o batima eh um frutinha

7.1.3. Controles de Fluxo

Controles de fluxo são comandos que vão testando algumas alternativas e de acordo com essas alternativas, vão executando comandos. Vamos ver aqui cada um deles!

7.1.3.1. Controle de fluxo com o if

Um dos comandos de controle de fluxo mais usados é certamente o if, que é baseado na lógica "se acontecer isso, irei fazer isso. Se não, irei fazer aquilo". Vamos dar um exemplo:

#!/bin/bash
if [ -e $linux ]
then
   echo 'A variável $linux existe.'
else
   echo 'A variável $linux não existe.'
fi

Exemplo 7.2. Exemplo de fluxo com o if

O que este pedaço de código faz? O if testa a seguinte expressão: Se a variável $linux existir, então (then) ele diz que que existe com o echo, se não (else), ele diz que não existe. O operador -e que usei é pré-definido e você pode encontrar a listagem dos operadores na tabela:

Operador Descrição
-eq Igual
!= Diferente
-gt Maior
-lt Menor
-o "Ou"
-d Se for um diretório
-e Se existir
-z Se estiver vazio
-f Se contiver texto
-O Se o usuário for o dono
-r Se o arquivo pode ser lido
-w Se o arquivo pode ser alterado
-x Se o arquivo pode ser executado

Tabela 7.1. Operadores de fluxo do bash

Vamos para alguns exemplos, assim entendemos melhor o uso:

#!/bin/bash
if [ -z $naoexisto ]
then
   echo 'A variável $naoexisto realmente não existe! Incrível.'
   echo -n 'Criando... '
   naoexisto="agoraeuexisto"
   echo 'feito.'
else
   echo 'Oops! Não era pra você existir!'
fi

Exemplo 7.3. Exemplo usando operadores

#!/bin/bash
a=1
b=2

if [ $a -lt $b ]
then
   echo '$a é menor que $b!'
elif [ $a -gt $b ]
then
   echo '$a é maior que $b!'
elif [ $a -eq $b ]
then
   echo '$a é igual a $b!'
fi

Exemplo 7.4. Exemplo usando operadores

#!/bin/bash
if [ -d "/etc" ]
then
   echo '/etc existe e é um diretório!'
   if [ -f "/etc/passwd" ]
   then
      echo '/etc/passwd existe e é um arquivo!'
   fi
else
   echo '/etc não existe :('
fi

Exemplo 7.5. Exemplo usando operadores

Note nos exemplos acima que citamos um "comando" não visto antes: o elif, que é uma combinação de else e if. Ao invés de fechar o if para criar outro, usamos o elif para testar uma expressão no mesmo comando if.

7.1.3.2. Outros comandos divertidos para se usar

Existem inúmeros comandos no Linux e para explicar todos, teríamos de publicar um verdadeiro livro. Você pode usar livremente qualquer comando texto disponível no seu Linux. Também se quiser, há muitas descrições na página de manual do bash, que pode ser acessada com o comando man bash.

Na tabela a seguir, você pode encontrar uma listagem de comandos para usar em sua shell script:

echo

Imprime um certo texto na tela, ou aonde você indicar (através de um redirecionador, veja em Seção 3.3.1.10, “cat - Exibe o conteúdo de um arquivo ou direciona-o para outro” para mais informações sobre redirecionadores.

read

Captura dados do usuário e coloca numa variável. O primeiro parâmetro passado é a variável. (Exemplo: read variavel)

exit

Finaliza o script. Caso seja passado algum numero depois, ele retorna esse número como a saída do comando. Este método geralmente serve para indicar se o comando foi bem sucedido ou não. (Exemplo: exit 1)

sleep

Dá uma parada em segundos no script, sendo os segundos o primeiro argumento. (Exemplo: sleep 15 faz o script parar por 15 segundos)

clear

Limpa a tela.

stty

Configura o terminal temporariamente. Útil por exemplo para não aparecer o que o usuário digita na hora de escrever uma senha. (Exemplo: stty -echo e stty echo)

tput

Altera o modo de exibição, como por exemplo as quantidades padrões de colunas de caracteres do terminal.

E assim seja, crie seus próprios scripts e facilite de uma vez só parte de sua vida no Linux!

7.1.3.3. Fluxo com o case

O case é para controle de fluxo, tal como é o if. Mas enquanto o if testa expressões não exatas, o case vai agir de acordo com os resultados exatos. Vejamos um exemplo:

case $1 in
   parametro1) comando1 ; comando2 ;;
   parametro2) comando3 ; comando4 ;;
   *) echo "Você tem de entrar com um parâmetro válido" ;;
esac

Exemplo 7.6. Exemplo usando controle de fluxo case

Aqui aconteceu o seguinte: o case leu a variável $1 (que é o primeiro parâmetro passado para o programa) e comparou com valores exatos. Se a variável $1 for igual à "parametro1", então o programa executará o comando1 e o comando2; se for igual à "parametro2", executará o comando3 e o comando4 e assim em diante. A última opção (*), é uma opção padrão do case, ou seja, se o parâmetro passado não for igual a nenhuma das outras opções anteriores, esse comando será executado automaticamente.

Você pode ver que, com o case fica muito mais fácil criar uma espécie de "menu" para o shell script do que com o if. Vamos demonstrar a mesma função anterior, mas agora usando o if:

if [ -z $1 ]; then
   echo "Você tem de entrar com um parâmetro válido"
   exit
elif [ $1 = "parametro1" ]; then
   comando1
   comando2
elif [ $1 = "parametro2" ]; then
   comando3
   comando4
else
   echo "Você tem de entrar com um parâmetro válido"
fi

Exemplo 7.7. Exemplo do equivalente do case usando if

Veja a diferença. É muito mais prático usar o case! A vantagem do if é que ele pode testar várias expressões que o case não pode. O case é mais prático, mas o if pode substituí-lo e ainda abrange mais funções. Note que, no exemplo com o if, citamos um "comando" não visto antes: o elif - que é uma combinação de else e if. Ao invés de fechar o if para criar outro, usamos o elif para testar uma expressão no mesmo comando if.

7.1.3.4. Fluxo com o for

O laço for vai substituindo uma variável por um valor e vai executando os comandos pedidos. Veja o exemplo:

for i in *
do
   cp $i $i.backup
   mv $i.backup /usr/backup
done

Exemplo 7.8. Exemplo usando controle de fluxo for

Primeiramente o laço for atribuiu o valor de retorno do comando "*" (que é equivalente a um ls sem nenhum parâmetro) para a variável $i, depois executou o bloco de comandos. Em seguida ele atribui outro valor do comando "*" para a variável $1 e reexecutou os comandos. Isso se repete até que não sobrem valores de retorno do comando "*". Outro exemplo:

for original in *; do
   resultado=`echo $original |
              tr '[:upper:]' '[:lower:]'`
   if [ ! -e $resultado ]; then
      mv $original $resultado
   fi
done

Exemplo 7.9. Exemplo usando controle de fluxo for

Aqui, o que ocorre é a transformação de letras maiúsculas para minúsculas. Para cada arquivo que o laço lê, uma variável chamada $resultado irá conter o arquivo em letras minúsculas. Para transformar em letras minúsculas, usei o comando tr. Caso não exista um arquivo igual e com letras minúsculas, o arquivo é renomeado para o valor da variável $resultado, de mesmo nome, mas com letras minúsculas.

Como os exemplos ilustram, o laço for pode ser bem útil no tratamento de múltiplos arquivos. Você pode deixá-los todos com letras minúsculas ou maiúsculas sem precisar renomear cada um manualmente, pode organizar dados, fazer backup, entre outras coisas.

7.1.3.5. Fluxo com o while

O while testa continuamente uma expressão, até que ela se torne falsa. Exemplo:

variavel="valor"
while [ $variavel = "valor" ]; do
   comando1
   comando2
done

Exemplo 7.10. Exemplo usando controle de fluxo while

O que acontece aqui é o seguinte: enquanto a "$variavel" for igual a "valor", o while ficará executando os comandos 1 e 2, até que a "$variavel" não seja mais igual a "valor". Se no bloco dos comandos a "$variavel" mudasse, o while iria parar de executar os comandos quando chegasse em done, pois agora a expressão $variavel = "valor" não seria mais verdadeira.

7.1.3.6. Fluxo com o until

Tem as mesmas características do while, a única diferença é que ele faz o contrário. Veja o exemplo abaixo:

variavel="naovalor"
until [ $variavel = "valor" ]; do
   comando1
   comando2
done

Exemplo 7.11. Exemplo usando controle de fluxo until

Ao invés de executar o bloco de comandos (comando1 e comando2) até que a expressão se torne falsa, o until testa a expressão e executa o bloco de comandos até que a expressão se torne verdadeira. No exemplo, o bloco de comandos será executado desde que a expressão $variavel = "valor" não seja verdadeira. Se no bloco de comandos a variável for definida como "valor", o until pára de executar os comandos quando chega ao done.

Vejamos um exemplo para o until que, sintaticamente invertido, serve para o while também:

var=1
count=0
until [ $var = "0" ]; do
   comando1
   comando2
   if [ $count = 9 ]; then
      var=0
   fi
   count=`expr $count + 1`
done

Exemplo 7.12. Exemplo usando controle de fluxo until

Primeiro, atribuímos à variável "$var" o valor "1". A variável "$count" será uma contagem para quantas vezes quisermos executar o bloco de comandos. O until executa os comandos 1 e 2, enquanto a variável "$var" for igual a "0". Então usamos um if para atribuir o valor 0 para a variável "$var", se a variável "$count" for igual a 9. Se a variável "$count" não for igual a 0, soma-se 1 a ela. Isso cria um laço que executa o comando 10 vezes, porque cada vez que o comando do bloco de comandos é executado, soma-se 1 à variável "$count" e quando chega em 9, a variável "$var" é igualada a zero, quebrando assim o laço until.

7.1.4. Usando vários scripts em um só

Pode-se precisar criar vários scripts shell que fazem funções diferentes, mas e se você precisar executar em um script shell um outro script externo para que este faça alguma função e não precisar reescrever todo o código? É simples, você só precisa incluir o seguinte comando no seu script shell:

. bashscript2

Isso executará o script shell "bashscript2" durante a execução do seu script shell. Neste caso ele será executado na mesma script shell em que está sendo usado o comando. Para utilizar outra shell, você simplesmente substitui o "." pelo executável da shell, assim:

sh script2
tcsh script3

Nessas linhas o script2 será executado com a shell sh e o script3 com a shell tcsh.

7.1.5. Variáveis especiais

Variável Função
$0 Nome do script que está sendo executado
$1-$9 Parâmetros passados à linha de comando
$# Número de parâmetros passados
$? Valor de retorno do último comando ou de todo o shell script. (o comando "exit 1" retorna o valor 1)
$$ Número do PID (Process ID)

Tabela 7.2. Lista de variáveis especiais

Você também encontra muitas variáveis, já predefinidas, na página de manual do bash (comando "man bash", seção Shell Variables).

7.1.6. Funções

Funções são blocos de comandos que podem ser definidos para uso posterior em qualquer parte do código. Praticamente todas as linguagens usam funções que ajudam a organizar o código. Vejamos a sintaxe de uma função:

funcao() {
   comando1
   comando2
   ...
}

Fácil de entender, não? A função funcionará como um simples comando próprio. Você executa a função em qualquer lugar do script shell e os comandos 1, 2 e outros serão executados. A flexibilidade das funções permite facilitar a vida do programador, como no exemplo final.

7.1.7. Exemplo final de shell-script

Agora vamos dar um exemplo de um programa que utilize o que aprendemos anteriormente.

#!/bin/bash
# Exemplo Final de Script Shell
Principal() {
   echo "Exemplo Final sobre o uso de scripts shell"
   echo "------------------------------------------"
   echo "Opções:"
   echo
   echo "1. Trasformar nomes de arquivos"
   echo "2. Adicionar um usuário no sistema"
   echo "3. Deletar um usuário no sistema"
   echo "4. Fazer backup dos arquivos do /etc"
   echo "5. Sair do exemplo"
   echo
   echo -n "Qual a opção desejada? "
   read opcao
   case $opcao in
      1) Transformar ;;
      2) Adicionar ;;
      3) Deletar ;;
      4) Backup ;;
      5) exit ;;
      *) "Opção desconhecida." ; echo ; Principal ;;
   esac
}
Transformar() {
   echo -n "Para Maiúsculo ou minúsculo? [M/m] "
   read var
   if [ $var = "M" ]; then
      echo -n "Que diretório? "
      read dir
      for x in `/bin/ls` $dir; do
         y=`echo $x | tr '[:lower:]' '[:upper:]'`
         if [ ! -e $y ]; then
            mv $x $y
         fi
      done
   elif [ $var = "m" ]; then
      echo -n "Que diretório? "
      read dir
      for x in `/bin/ls` $dir; do
         y=`echo $x | tr '[:upper:]' '[:lower:]'`
         if [ ! -e $y ]; then
            mv $x $y
         fi
      done
   fi
}
Adicionar() {
   clear
   echo -n "Qual o nome do usuário a se adicionar? "
   read nome
   adduser nome
   Principal
}
Deletar() {
   clear
   echo -n "Qual o nome do usuário a deletar? "
   read nome
   userdel nome
   Principal
}
Backup() {
   for x in `/bin/ls` /etc; do
      cp -R /etc/$x /etc/$x.bck
      mv /etc/$x.bck /usr/backup
   done
}
Principal

Exemplo 7.13. Exemplo final de shell-script