Artigo do tipo Exemplos Práticos
Autores: Ivani Nascimento e Pathiene Gerstenberger
Introdução ao Shell Script
Este artigo tem por objetivo introduzir fundamentos de Shell script aos administradores de sistema, possibilitando a automatização de tarefas repetitivas e até mesmo a personalização do ambiente. O shell script não é exatamente uma linguagem de programação, mas tem a vantagem de ser mais simples, pois trabalha com uma gama de utilitários para tarefas como consulta de informações, manipulação de textos, elaboração de relatórios, envio de e-mails, e assim por diante. Em razão da diversidade de solução para um mesmo problema, e sendo a criatividade o limite para elaborar Shell script, este artigo não cobre todos os tópicos relacionados ao assunto. Serão apresentadas definições do que é necessário saber para criação de scripts, permitindo que o leitor esteja apto para criar seus próprios programas.


Em que situação o tema é útil
Através dos tempos, o homem sempre tentou resolver seus problemas com a maior rapidez e com o menor esforço possíveis. A busca pela automatização de tarefas repetitivas possibilitou a invenção de instrumentos como a roda, a alavanca, o carro e até o computador. Entretanto, as tarefas que devem ser realizadas pelo computador de forma automática têm de ser planejadas e colocadas em uma sequência de ações para serem posteriormente executadas. A vantagem no Linux é que praticamente tudo pode ser automatizado por meio de script, utilizando shells nativos no sistema operacional, onde é possível criar desde rotinas simples para backup até programas que realizam extração de dados para serem utilizados na confecção de relatórios. Seja você um administrador de sistemas, programador ou usuário final, em algum momento irá se deparar com situações em que um simples shell script será utilizado para economia de tempo ou, ainda, para automatizar alguma tarefa importante.

Todo administrador de sistema, programador ou até mesmo usuário final, um dia se depara com a necessidade de criar um Shell script simples (ou não) para economizar tempo e esforço, ou ainda automatizar uma tarefa repetitiva, que pode ser desde um alias (apelido) para um comando utilizado com frequência até uma tarefa mais complexa, como extração de dados para criação de relatórios.

Mas o que é exatamente o Shell? Todo Sistema Operacional (SO) possui um shell, que interpretará os comandos do usuário, como copiar ou remover arquivos a partir de uma interface gráfica (GUI – Graphical User Interface) utilizando o mouse, ou a partir do modo linha de comando (CLI – Command-Line Interface) em um terminal. Assim, o Shell, antes de tudo, é uma interface para comunicação entre o usuário e o sistema operacional, recebendo como input (entrada) comandos que são convertidos em instruções que o SO entenda e execute. Quando um comando é emitido, o shell passa essa solicitação para o kernel traduzir em linguagem apropriada à plataforma que está sendo utilizada. Dessa forma, o sistema tem um maior controle sobre o hardware, impedindo acessos indevidos por parte dos usuários.

Além de shells de sistema operacional baseados em texto, é possível encontrar shells para interface gráfica (como o Windows Explorer no Windows, ou o Nautilus no Linux), shells para linguagens (como Perl/Python shell), ou ainda shells para fins específicos, como o goosh, por exemplo, que funciona como um front-end para navegação web. Com ele é possível realizar pesquisa via linha de comando com o Google por meio de comandos como search, more e go. A Figura 1 apresenta um exemplo de uso do goosh.

Figura 1. Shell goosh: exemplo de pesquisa Google via linha de comando.

O Unix popularizou a separação do shell (parte do sistema que permite que os comandos sejam digitados e interpretados) de todo o resto: o sistema de entrada/saída, gerenciamento de memória/CPU, entre outras tarefas que o SO executa para que o usuário não tenha que se preocupar. Mas esse conceito foi o início de uma revolução, na qual o shell era mais um programa que rodava no Unix e contava com pelo menos dois tipos: o Bourne Shell (sh), descendente do Thompson Shell (original do Unix, que possuía estruturas básicas de controle e era minimalista a ponto de as declarações if e goto serem implementadas como comandos externos) e o C Shell (csh) desenvolvido por Billy Joe da Universidade de Berkeley, que tinha por objetivo criar uma linguagem de script que fosse semelhante à linguagem C. Ao longo dos anos, outras alternativas surgiram, como o Korn Shell e o Bourne Again Shell, que será utilizado neste artigo.

Quando se fala em Shell, geralmente imagina-se apenas um terminal complexo que aceita comandos, porém o shell faz mais que isso: é possível ter um histórico dos comandos digitados que podem ser editados e reutilizados, personalizar o ambiente definindo comandos próprios (criação de alias), criar atalhos, etc. E, como se não fosse o bastante, o shell também é programável, possibilitando que sequências de comandos digitados repetidamente possam ser colocadas em um arquivo e transformadas em um programa, comumente chamado de script. Compactar arquivos de log do sistema para backup, renomear uma lista de arquivos, localizar arquivos criados há mais de 30 dias no disco e remover, são exemplos de tarefas rotineiras que podem ser automatizadas por meio de um script.

Com base neste cenário, o objetivo deste artigo é proporcionar ao usuário que já possui certo domínio na utilização do Linux, uma visão sobre o ambiente shell e a familiarização com as características básicas (estrutura, variáveis, parâmetros) para a criação de scripts utilizando o shell Bash.

Definição inicial de Shell

De uma forma geral, shell é o nome comum para um conjunto de programas que tem o propósito de fornecer uma interface interativa com o usuário, desempenhando entre outras, as seguintes funções:

· Ler a linha de comando do usuário;

· Separar o comando e parâmetros passados;

· Executar o comando (interno ou não);

· Esperar que o processo seja finalizado;

· Retornar o controle para o usuário.

Tipos de shell

O shell, como já informado, é o programa que permite a interação do usuário com o sistema. No início, o sh era o shell padrão utilizado para criação de scripts e interpretação de comandos, porém, atualmente existem diversos tipos de shell, cada um com seus próprios recursos, capacidades e limitações, sendo que em quase todas as distribuições do Linux, o bash é o shell padrão. A seguir são apresentadas as descrições dos principais tipos de shells.

sh

Bourne shell, ou simplesmente sh, foi o shell padrão do Unix Versão 7, que substituiu o Thompson shell, cujo arquivo executável tinha o mesmo nome, sh. Desenvolvido por Stephen Bourne, dos laboratórios AT&T, foi lançado em 1977 junto com o Unix Versão 7 distribuído para as universidades. Shell com maior portabilidade para criação de scripts, introduziu uma sintaxe compacta para canalização e redirecionamento (|, <> e >>), suporte para comandos sequenciais (utilizando o ';') e recursos como histórico e edição de comandos, alias, entre outros.

csh/tcsh

O C shell (csh) foi desenvolvido para sistemas UNIX Berkeley Software Distribution (BSD) por Bill Joe, com o objetivo de criar uma linguagem de script que fosse semelhante à linguagem C. Um recurso útil introduzido por Bill Joe no csh foi o histórico de comandos, que permitia que o usuário revisasse e selecionasse facilmente comandos anteriores para serem executados. Após cinco anos, Ken Greer, da Universidade Carnegie Mellon, criou o tcsh, compatível com scripts C Shell e com recursos como auto completar melhorado em relação ao csh, correção ortográfica e edição de linha de comando.

ksh

O Korn Shell (ksh) foi projetado por David Korn na mesma época que o tcsh, sendo compatível com o Bourne Shell. Inclui recursos como gerenciamento do histórico de comandos, uso de alias, auto completar, controle de jobs e interação com outras linguagens como awk, perl e tcl. Em função do seu licenciamento inicial, era software proprietário até 2000, quando foi lançado como software livre (sob a licença Common Public License).

bash

Bourne-Again Shell (trocadilho com Stephen Bourne, autor do Unix Shell sh), ou Bash, é o interpretador de comandos para sistemas GNU. Desenvolvido por Brian Fox, é padrão na maioria das distribuições Linux atuais, como Red Hat, Suse, Debian e outras, sendo amplamente compatível com sh, além de incorporar características úteis do ksh e do csh, oferecendo melhorias funcionais sobre o sh tanto para uso interativo como para programação. Atualmente, funciona em quase todas as versões do Unix e em alguns outros sistemas operacionais, como Linux, Windows, Darwin e outros. Dentre as características, pode-se citar o histórico de comandos, controle de tarefas agendadas, funções de shell, aliases, operações aritméticas, personalização, suporte para expressões regulares (semelhante ao Perl), arrays associativos e muitas outras. Este shell será o objeto de estudo deste artigo em um ambiente GNU/Linux, modo texto.

Arquivos de configuração do Shell

Toda sessão no Linux (seja modo gráfico ou texto) se inicia com a solicitação de um nome de login e senha, e uma vez que essas informações estejam corretas, um shell é invocado e fará a leitura de alguns arquivos de configuração do ambiente antes de entregar o controle para que o usuário possa interagir com o sistema por meio de comandos.

O bash é o shell padrão nas distribuições Linux, e é utilizado caso o arquivo /etc/passwd não especifique outro shell para o usuário, ou indique o caminho de um script a ser executado após o usuário fazer login no sistema. A Listagem 1 apresenta o conteúdo do arquivo /etc/shells de um sistema Debian GNU/Linux Squeeze, que contém uma lista de interpretadores de comando (shells) válidos no sistema, lembrando que este conteúdo pode variar entre as distribuições.

Listagem 1. Conteúdo do arquivo /etc/shells.


# cat /etc/shells 
  # /etc/shells: valid login shells
  /bin/csh
  /bin/sh
  /usr/bin/es
  /usr/bin/ksh
  /bin/ksh
  /usr/bin/rc
  /usr/bin/tcsh
  /bin/tcsh
  /usr/bin/esh
  /bin/dash
  /bin/bash
  /bin/rbash 

Existem duas maneiras de se verificar qual Shell está sendo utilizado no ambiente: 1) consultando a variável de ambiente SHELL; e, 2) verificando o último parâmetro definido para o usuário no arquivo /etc/passwd, conforme apresentado na Listagem 2.

Listagem 2. Consultando o Shell utilizando no ambiente.


Utilizando o comando echo:
  # echo $SHELL
  /bin/bash
   
  Trecho do conteúdo do arquivo /etc/passwd:
  # cat /etc/passwd
  root:x:0:0:root:/root:/bin/bash
  bin:x:2:2:bin:/bin:/bin/sh
  ivani:x:1000:1000:Ivani,,,:/home/ivani:/bin/bash
  pgoncales:x:1009:1004:Pathiene,,,:/home/pgoncales:/bin/bash 

Os arquivos de configuração do shell definem, entre outras coisas, os aliases, tipo de prompt e variáveis de ambiente, e podem ser classificados como globais, influenciando na configuração do ambiente de todos os usuários ou locais, atuando somente na sessão do usuário. A leitura dos arquivos de configuração obedece à seguinte ordem:

1. Quando um shell de login interativo (isto é, que requer autenticação) é iniciado, o arquivo /etc/profile, caso exista, é executado. Esse arquivo contém configurações globais que são aplicadas a todos os usuários do sistema;

2. Em seguida, o shell procura pelos seguintes arquivos: ~/.bash_profile, ~/.bash_login ou ~/.profile no diretório home do usuário, executando o primeiro que estiver disponível com permissão de leitura. Caso algum dos arquivos exista, mas não possa ser lido, será reportado um erro para o usuário;

3. Durante a sessão, caso seja invocado um novo shell através da ação do usuário executando o comando xterm para abrir uma nova janela de terminal, o arquivo ~/.bashrc é lido e executado. Esse arquivo contém as configurações de personalização do ambiente, tais como definição de alias, cores do prompt, entre outras;

4. Quando o shell for finalizado (usuário efetuar o logout), será lido e executado o arquivo ~/.bash_logout, caso exista.

Uma vez que os arquivos de configuração do ambiente foram lidos, o shell entrega o controle para o usuário e está pronto para ser utilizado para a execução de tarefas de administração de sistemas. Estas tarefas podem variar desde a criação de usuários e grupos, leitura de logs, até a programação em shell e a criação de scripts.

Shell Script

Como já explanado neste artigo, o shell é um interpretador de comandos, sendo uma interface para comunicação entre o usuário e o kernel. Quando o usuário digita um comando, este é analisado pelo shell, que verifica as permissões, resolve redirecionamentos, substitui variáveis e metacaracteres, caso existam, e finalmente solicita ao kernel a execução das ações correspondentes ao comando em questão. Imaginando que um usuário necessite executar sempre esse mesmo conjunto de comandos, seria ideal automatizar a rotina por meio da utilização de um script. Portanto, quando existe a necessidade de executar a mesma tarefa diariamente, como criar um backup do diretório /etc em vez de digitar os comandos repetidamente, basta colocá-los na sequência correta dentro de um arquivo texto e transformá-lo em um roteiro, que será lido, interpretado e executado pelo shell. Esse roteiro que foi descrito, comumente chamado de shell script, é frequentemente utilizado para tarefas de administração de sistemas ou para combinar programas existentes para concluir um trabalho específico.

Aplicações do uso do Shell script

É possível utilizar o shell de forma interativa e não-interativa. Quando o usuário realiza uma operação simples, como a execução de algum comando como ls, cat ou cd, o shell está sendo utilizado interativamente. Porém, essa forma de uso pode ser cansativa, se as operações são repetidas continuamente, sendo mais prático colocar esses comandos em um arquivo para reaproveitamento, isto é, criar um script.

O uso automatizado (não-interativo) do Shell Script (utilizando arquivos com comandos) deve ser feito quando for necessário criar uma rotina a ser executada por todos os usuários sem necessitar digitá-la comando a comando; procedimentos complexos usando muitas linhas de comando; execução de uma tarefa planejada para ocorrer em uma determinada data; interação de informações de vários sistemas existentes; tarefas rotineiras; etc.

Criando um script simples

Antes de partir para a criação de scripts elaborados, vejamos como criar um script simples, com a finalidade de contar quantos usuários estão conectados no sistema. Para criar este script, pode ser utilizado o comando who, que exibe os usuários conectados linha a linha, conforme mostra a saída do comando na Listagem 3.

Listagem 3. Verificação de usuários conectados no sistema.



# who
  ivani    tty1         2013-02-27 02:42
  root     pts/0        2013-02-27 02:42 (192.168.1.103)
  pgoncales pts/1       2013-02-27 02:44 (192.168.1.103) 

No exemplo, somente três usuários estão conectados, mas em um sistema multiusuário, essa listagem pode ser maior e a rolagem da tela poderia não permitir contar todos os usuários de uma forma manual. Nesse caso, o usuário pode optar por automatizar essa tarefa, mas antes é necessário contar o número de usuários. Isso pode ser feito combinando o comando who com o comando wc (word count), que conta linhas, palavras e caracteres, conforme apresentado a seguir:

# who | wc -l

3

A opção –l (line) do comando wc, exibe a quantidade de linhas, e o símbolo | (pipe) cria um canal entre dois comandos: a saída do comando who torna-se a entrada do comando wc, que imprime a quantidade de usuários conectados.

Sabendo o número de usuários, pode-se automatizar a tarefa, bastando para isso escrever os comandos em um arquivo de texto normal e torná-lo executável utilizando o comando chmod, conforme o exemplo na Listagem 4. O comando cat é utilizado para escrever em arquivo texto, exibir seu conteúdo ou ainda concatenar arquivos. Vale lembrar que este comando não substitui o editor de textos, uma vez que sua função nesse exemplo é apresentar mais uma forma de realizar a tarefa de criação e edição de arquivos texto no terminal.

Listagem 4. Automatizando a contagem de usuários.


Cria o arquivo e copia o conteúdo do arquivo utilizando o comando cat:
  # cat > conta_users
   
  Conteúdo do script
  who | wc -l
   
  CTRL-D para salvar e finalizar a edição do script
  ^D
  Torna o arquivo executável com a opção +x do comando chmod:
  # chmod +x conta_users 
  Executa o script no diretório onde foi criado, 
  sendo que a saída deve retornar o número de usuários:
  # ./conta_users
  3

Estrutura de um script

Neste tópico, será apresentada a estrutura de um script, isto é, comandos colocados em um arquivo para serem executados posteriormente. Os scripts foram elaborados em um servidor com o sistema operacional Debian GNU/Linux 6.0, e o editor de textos utilizado foi o vim, porém o usuário pode utilizar qualquer editor sobre o qual tenha conhecimento.

Inicialmente, para deixar o estudo de shell script organizado, é interessante criar um diretório para armazenar os scripts que serão criados, conforme o exemplo apresentado na Listagem 5.

Listagem 5. Criando diretório para armazenar os scripts criados.


Entra no diretório home do usuário
  $ cd /home/aluno 
  Cria o diretório scripts:
  $ mkdir scripts 
  Verifica o local onde se encontra (pwd) e lista o conteúdo do diretório:
  $ pwd
  /home/aluno
  $ ls -l
  drwxr-xr-x 2 aluno aluno 4096 Feb 27 04:10 scripts 
  Entra no diretório onde serão armazenados o scripts:
  $ cd scripts/
  $ pwd
  /home/aluno/scripts 

No exemplo, o diretório script foi criado dentro do home do usuário aluno. Após a criação, listou-se o conteúdo do home e em seguida foi executado o comando cd para entrar no diretório recém-criado. A partir de agora, os scripts apresentados neste artigo serão criados utilizando o usuário aluno dentro do diretório /home/aluno/scripts. Vale lembrar que este é um usuário comum; caso seja necessária a utilização do usuário root, o fato será mencionado.

No primeiro contato com shell script (vide Listagem 4), foi apresentado um exemplo de script simples, porém não foi utilizado qualquer tipo de estrutura convencional em sua criação (definição de shell padrão, comentários, etc.). A Listagem 6 apresenta um script que exibe a data e a hora, lista o conteúdo do diretório e exibe o espaço livre em disco utilizando práticas comuns na programação em shell.

Listagem 6. Exemplo de estrutura de shell script.


$ vim shellscript.sh
  #!/bin/bash
  #Script de teste
  date
  ls -l
  df –h 

Ainda que o Shell Script seja escrito em um editor de textos, o arquivo geralmente segue uma extensão padrão, que é a extensão .sh (shell), que pode ser definida ao criar o arquivo. Um fator importante que deve ser considerado na hora de definir o nome do script é evitar o uso de letras maiúsculas, acentuação e espaçamento. Sendo assim, o ideal é utilizar nomes com letras minúsculas, que podem ser separados por - (hífen) ou _ (underline) se necessário, como shell-script.sh ou shell_script.sh, conforme o exemplo a seguir:

$ vim shellscript.sh

Este comando utilizou o editor de textos vim para criar o arquivo shellscript.sh. É neste arquivo que serão inseridos os comandos do script.

Uma vez que existem diversos shells, é necessário saber o shell padrão utilizado pelo sistema operacional, pois a primeira linha do script indica qual shell será utilizado para interpretar os comandos. Para saber qual o shell padrão, basta executar:

$ echo $SHELL

Feito isso, adicione os comandos apresentados na Listagem 7 no arquivo shellscript.sh.

Listagem7. Conteúdo do script shellscript.sh.


#!/bin/bash
  #Script de teste
  date
  ls -l
  df -h 

Como já informado, a primeira linha é utilizada por padrão para definir o shell. A segunda linha é apenas um comentário. O sinal “#” serve para permitir a adição de comentários dentro do script. Esses comentários não são impressos na tela quando o script é executado.

Da terceira até a quinta linha são executados três comandos: date, ls -l e df -h. O comando date sem parâmetros imprime a data e a hora corrente, o comando ls seguido do parâmetro -l (list) lista o conteúdo do diretório corrente, e o comando df seguido do parâmetro -h (human) mostra o espaço que o disco possui separado pelas partições.

Depois que os comandos foram inseridos no script é necessário salvar as informações, fechar o editor e dar a permissão de execução ao arquivo:

$ chmod +x shellscript.sh

O comando chmod seguido do parâmetro +x é o que definirá a permissão de execução ao arquivo shellscript.sh. Agora que o script já está pronto para ser executado, pode-se utilizar duas maneiras para iniciá-lo, a saber:

$ sh shellscript.sh

ou

$ ./shellscript.sh

O resultado da execução do script será o mesmo nas duas opções. A diferença é que, ao empregar o comando sh, é solicitado ao shell que execute o script mesmo que você esteja em um diretório diferente do local original do script. Já o ./ executa o script pelo binário; portanto é necessário que ao utilizar esse método, você esteja no mesmo diretório em que o script foi criado.

Um ponto curioso é que, ao usar o comando sh para executar um script, não é obrigatório ter a permissão de execução. Neste caso, independentemente das opções que você possui para executar um script, os resultados apresentados serão os mesmos.

Quando se está criando um script, é interessante imprimir mensagens na tela para apresentar o que será executado. O comando que imprime as mensagens é o echo. Com este comando também é possível verificar o valor das variáveis do sistema. Por exemplo, o comando echo $BASH apresenta o valor da variável BASH. A mesma informação pode ser obtida com a variável SHELL. Ambos os resultados apresentam o shell padrão que está sendo utilizado para executar os comandos.

Nota-se que para visualizar os valores das variáveis é necessário utilizar o sinal de ‘$’ antes do nome da variável, caso contrário, o comando echo apenas imprimirá o que foi escrito no shell, conforme apresentado na Figura 2.

Figura 2. Visualização do conteúdo da variável.

Quando o script precisar de alterações, basta editar o arquivo novamente e inserir ou alterar os comandos necessários. As alterações apresentadas na Listagem 8 farão com que sejam impressas mensagens na tela quando o script for executado. Para isto, empregaremos o comando echo.

Listagem 8. Utilizando echo para exibir mensagens na tela.


$ vim shellscript.sh
  #!/bin/bash
  #Script de teste
  echo “Data”
  sleep 1
  date
  echo “Listagem do diretório corrente”
  sleep 2
  ls -l
  echo “Espaço em Disco”
  sleep 3
  df -h 

Outro comando adicionado é o sleep, que faz uma breve pausa em segundos. No script, o comando sleep 1 fará uma pausa de 1 segundo e o comando sleep 3 fará uma pausa de três segundos.

Manipulando variáveis

Assim como todas as linguagens de programação, o shell também permite o uso de variáveis na construção de scripts. Para facilitar a compreensão de como utilizá-las, a Listagem 9 demonstra um exemplo.

Listagem 9. Trabalhando com variáveis.


#!/bin/bash
  #Script de teste
  echo -e "Executando script...\n Permite que 
  os comandos a seguir sejam executados? [s/n] "
  read RESP
  test "$RESP" = "n" && exit
  echo “Data”
  sleep 1
  date
  echo “Listagem do diretório corrente”
  sleep 2
  ls -l
  echo “Espaço em Disco”
  sleep 3
  df -h 

Na primeira linha em destaque na Listagem 9 é possível notar o comando echo, utilizado para exibir mensagens na tela. Nesse ponto, quando o script for executado, a seguinte mensagem será exibida:

Executando o script...

Permite que os comandos a seguir sejam executados? [s/n]

Note que o comando echo está acompanhado da opção -e e embora a mensagem para o usuário esteja em apenas uma linha, observe que ao executar o script, a sentença foi quebrada em duas linhas. Esta opção permite que o echo interprete caracteres especiais que não são impressos, como por exemplo, a quebra de linha (\n) ou ainda tabulações horizontais (\t) ou verticais (\v).

Continuando com a análise das linhas em destaque na Listagem 9, após exibir a mensagem na tela, o script aguarda por uma ação do usuário que definirá, neste caso, se a execução do script deve prosseguir ou não. O comando read faz a leitura do que foi digitado pelo usuário e armazena este resultado em uma variável chamada RESP, conforme a linha a seguir:

read RESP

O valor armazenado na variável $RESP será analisado pela seguinte condição: se a resposta digitada pelo usuário for igual a n, então o script será finalizado. Se a resposta do usuário for s, então o script seguirá com a execução. Esta condição está representada na linha:

test "$RESP" = "n" && exit

O comando test é um condicional (assim como o if, case, entre outros) e o operador lógico “&&” significa que o segundo comando só será executado se o primeiro comando der uma resposta positiva.

A Figura 3 apresenta a execução do script.

Figura 3. Exemplo de script com interação do usuário.

Como pode ser observado, o script foi executado duas vezes para apresentar os diferentes resultados que podem ser obtidos. Na primeira execução, o usuário digitou a opção n e o script foi interrompido. Já na segunda, o usuário digitou s e o script executou todos os comandos que estavam configurados.

Trabalhando com parâmetros

Em algumas situações, os scripts precisam de parâmetros para que possam ser executados. Por exemplo, vamos supor que você crie um script que faça a atualização de um arquivo. Como o nome do arquivo pode ser diferente a cada atualização, é necessário passar através de um parâmetro o arquivo novo que o script terá que utilizar.

Ao passar o parâmetro para um script, este valor é armazenado em uma variável de modo que dentro do script seja possível trabalhar com esta variável da maneira que você preferir.

A passagem de parâmetros é feita no ato da chamada para a execução do script. Para auxiliar nessa tarefa, são utilizadas variáveis pré-definidas que são tratadas de maneira especial pelo shell. Esse tipo de variável pode ser utilizado para saber, por exemplo, quantos parâmetros foram passados para o script em linha de comando, nome do script, número do processo, etc. A Tabela 1 apresenta uma lista dos principais parâmetros e sua descrição.

Tabela 1. Lista dos principais parâmetros especiais do shell.

A seguir, serão apresentados alguns exemplos de scripts que utilizam passagem de parâmetros e código de retorno.

$1 - $9 – Passagem de parâmetros

O script demonstrado na Listagem 10 tem como objetivo apresentar as funções mais básicas quando se trabalha com parâmetros em shell script, que são: o envio de um parâmetro, o armazenamento desta informação e como ela é tratada.

Listagem 10. Exemplo de script que utiliza passagem de parâmetro.


$ vim parametros.sh
   
  1 #!/bin/bash
  2 #Script que faz a leitura de parâmetros digitados pelo usuário
  3 echo "Os parâmetros digitados são:"
  4 echo $1
  5 echo $2
  6 echo $3

Ao executar o script, os parâmetros devem ser passados juntamente com o nome do script, conforme o exemplo a seguir:

./parametros.sh inframagazine shell script

A Figura 4 apresenta o resultado da execução do script.

Figura 4. Execução de script utilizando passagem de parâmetros.

Na Listagem 10, as linhas foram numeradas para facilitar a explicação de todo o script. A primeira linha define qual o bash padrão que está sendo utilizado para executar e interpretar os comandos no script. A segunda linha é apenas um comentário para representar o que o script executará. O comando echo imprimirá a mensagem na terceira linha e, por fim, nas linhas 4, 5 e 6, o comando echo fará a leitura de cada variável, em que cada uma delas recebeu o valor do parâmetro digitado no início do script.

$# – Número total de parâmetros

O uso deste parâmetro fornece o número total de argumentos passados para o shell script na linha de comando. É utilizado em estruturas de repetição (o que será abordado mais adiante) para processamento de opções e argumentos. O script da Listagem 10 apresentou os parâmetros digitados pelo usuário, e com uma pequena alteração, o mesmo script mostrará a quantidade e os parâmetros passados na linha de comando, conforme demonstrado a seguir:

1 #!/bin/bash

2 # Script que exibe a quantidade e parâmetros passados

3 echo -e "Os $# parâmetros digitados são: \n $1 \n $2 \n $3"

No exemplo, o script da Listagem 10 foi reduzido a apenas uma linha de execução, onde serão exibidos a quantidade e os parâmetros passados pelo usuário na linha de comando. Sendo assim, o resultado obtido ao executar este script será:

$ ./parametros.sh inframagazine shell script

Os 3 parâmetros digitados são:

inframagazine

shell

script

$? – Código de retorno

Todo comando executado no shell retorna um valor depois que é encerrado. Este valor é definido como o estado de saída ou código de retorno e pode auxiliar na análise para saber se um comando foi executado corretamente. Se o código de retorno for 0 (zero), significa que o comando foi executado com sucesso.

Por exemplo, quando o usuário deseja copiar um arquivo, ele utiliza o comando cp. Quando a execução deste comando é encerrada, o shell armazena o estado de saída do cp na variável $?. Assim, para saber se o comando foi executado corretamente, basta verificar se o valor da variável $? é igual a zero. A Listagem 11 apresenta alguns exemplos relacionados.

Listagem 11. Exemplos de análise do código de retorno ao executar cópia de arquivos.


Cópia de um arquivo existente:
  # cp –v /etc/hosts /tmp
  `/etc/hosts' -> `/tmp/hosts' 
  Código de retorno indica sucesso:
  # echo $?
  0 
  Cópia de um arquivo inexistente com mensagem de erro do comando cp:
  # cp –v /etc/host /tmp/
  cp: cannot stat `/etc/host': No such file or directory 
  Código de retorno indica falha:
  # echo $?
  1

Expressões aritméticas

As expressões aritméticas podem ser utilizadas no shell script para efetuar operações matemáticas básicas como adição, subtração, multiplicação e divisão, além de diversas outras opções. Quem auxilia na execução dessas expressões é o comando expr (evaluate expressions), conforme apresentado no comando a seguir:

$ expr 2 + 2

4

Este comando trata os argumentos que são apresentados a ele e faz cálculos ou diversas outras ações que podem ser utilizadas, conforme pode ser visto na Tabela 2.

Tabela 2. Argumentos do comando expr.

Vale ressaltar que este comando trabalha apenas com números, portanto, na tentativa de realizar junções como a + a, será retornado um erro, conforme demonstrado na Figura 5.

Figura 5. Erro produzido ao utilizar comando expr com letras.

Na Listagem 12 é demonstrado um script que irá executar a soma entre dois números utilizando o comando expr, que pegará os valores para efetuar os cálculos por meio de parâmetros.

Listagem 12. Exemplo de script com a operação aritmética soma.


$vim soma.sh
  1 #!/bin/bash
  2 #Script que faz a soma entre dois números que devem ser passados por parâmetro
  3 echo "Os números digitados são:"
  4 echo $1
  5 echo $2
  6 echo "O resultado da soma destes dois números é: "
  7 soma=$(expr $1 + $2)
  8 echo $soma 

Na linha 1 está definido o shell padrão e na linha 2 foi definido um breve comentário sobre o objetivo do script. As linhas 3 a 6 imprimem mensagens na tela com o comando echo. Para indicar que uma variável receberá um valor que será o resultado de um comando, é necessário utilizar a estrutura $(comando). Na linha 7 é armazenado um valor na variável “soma”. O valor que esta variável recebe vem do cálculo realizado pelo comando expr.

A linha 8 apenas imprime o resultado da soma. Por fim, aplica-se a permissão de execução e basta rodar o script conforme apresentado na Figura 6.

Figura 6. Execução do script de operação aritmética.

Controle de fluxo

Uma das principais vantagens na utilização de um shell script é a possibilidade de agilizar a execução de tarefas de alta demanda, tais como manipulação de listas de dados, criação de usuários e grupos, backups, definição de quotas em disco, entre outras. Este tópico tratará de estruturas de controle de fluxo que dão a inteligência para o script decidir o que fazer a partir da utilização de controles condicionais (estruturas de decisão) ou loops (estruturas de repetição).

Estruturas Condicionais

Estruturas condicionais são recursos que possibilitam a tomada de decisão a partir de um teste que verifica se a condição para execução de determinado trecho de código é verdadeira ou falsa. Dependendo da análise, tal trecho poderá ou não ser executado.

if

Em toda linguagem de programação existe um tipo de estrutura para tomada de decisões. O if, um comando condicional popular, executa um bloco de comandos caso uma determinada expressão seja verdadeira (if); senão (else) abre um bloco de comandos para serem executados caso a expressão em teste seja falsa. No caso da necessidade de criação de ifs aninhados, isto é, um if dentro de outro, pode-se utilizar o comando elif (junção de else e if), que substitui a cadeia de else/if, melhorando a legibilidade do código e otimizando a execução do script. A Listagem 13 apresenta a sintaxe para utilização do if e elif.

Listagem 13. Sintaxe da estrutura condicional if e elif.


if condição 
      then
            Bloco de comandos 1
      elif condição 
      then
            Bloco de comandos 2
      else
            Bloco de comandos 3
  fi 

Na sintaxe apresentada, a primeira condição é testada. Se retornar verdadeiro, o bloco de comandos 1 será executado, senão, o fluxo de execução passará para a próxima condição, que é o elif. Dentro do elif existe uma segunda condição, e se esta for verdadeira, então o bloco de comandos 2 será executado. Se nenhuma das condições anteriores for verdadeira, a execução passará para o bloco de comandos 3.

A seguir, são apresentados dois exemplos de uso do if combinados com recursos já vistos no artigo, como interação com o usuário por meio do comando read, substituição de variáveis e utilização de parâmetros. As linhas do script foram numeradas para facilitar a compreensão.

O exemplo da Listagem 14 testa se a string digitada pelo usuário é igual ao conteúdo da variável de ambiente $USER, comparando se o nome de usuário digitado é o mesmo que está conectado no momento da execução do programa.

Listagem 14. Exemplo de uso do if.


$ vim usuario.sh
  1 #!/bin/bash
  2 echo "Teste com o comando if"
  3 echo "Entre com seu nome de usuário: "
  4 read NOME
  5 if [ $NOME = "$USER" ]
  6  then
  7   echo "Bem vindo $USER"
  8  else
  9   echo "Usuário não reconhecido"
  10 fi 

Considerando que você está no diretório scripts criado no início do artigo, após editar o arquivo usuario.sh, é preciso salvar e dar permissão de execução para o mesmo com o comando chmod:

$ chmod +x usuario.sh

Na análise do código, verifica-se que o comando echo exibe mensagens na tela e solicita que o nome do usuário seja digitado. A entrada é recebida pelo comando read e armazenada em uma variável chamada NOME, conforme o trecho do código a seguir:

2 echo "Teste com o comando if"

3 echo "Entre com seu nome de usuário: "

4 read NOME

Em seguida, o if testa se o nome que o usuário digitou é o mesmo que está armazenado na variável $USER, que é responsável por guardar o nome do usuário conectado na máquina no momento.

Cabe dizer aqui que o teste realizado pelo if é feito por meio do comando test, mencionado anteriormente neste artigo, e que tem por função processar a expressão condicional, retornando um código 0 ou 1. No exemplo, o comando test é representado por colchetes ([ ]) e a expressão a ser testada deve ser separada por espaços, conforme o código apresentado a seguir:

5 if [ $NOME = "$USER" ]

6 then

7 echo "Bem vindo $USER"

Caso o conteúdo da variável $NOME seja igual ao da variável $USER, a mensagem de boas vindas com este nome será exibida na tela, conforme a execução do script no bloco if, apresentado na Listagem 15.

Listagem 15. Execução do script usuario.sh no bloco if.


Execução do script em linha de comando:
  aluno@lab:~/scripts$ ./usuario.sh
  Teste com o comando if
  Entre com seu nome de usuário: 
  aluno
  Bem vindo aluno 
  Comando para verificar o conteúdo da variável $USER:
  aluno@lab:~/scripts$ echo $USER
  aluno 

Se o conteúdo da variável $NOME for diferente do conteúdo armazenado na variável $USER, será exibida a mensagem “Usuário não reconhecido” na tela e o script será finalizado com o comando fi, como indica o trecho do código:

8 else

9 echo "Usuário não reconhecido"

10 fi

A Listagem 16 apresenta a execução do script no bloco else.

Listagem 16. Exemplo do script usuario.sh no bloco else.


aluno@lab:~/scripts$ ./usuario.sh 
  Teste com o comando if
  Entre com seu nome de usuário: 
  asmith
  Usuário não reconhecido 

A seguir, a Listagem 17 apresenta o uso do if e do elif em um exemplo em que é solicitado ao usuário que informe dois números inteiros. Após a comparação, é exibida uma mensagem na tela, informando se os números são iguais ou se um é menor ou maior que o outro.

Listagem 17. Exemplo de uso do if e do elif.


$ vim compara_numeros.sh
   1  #!/bin/bash
   2  if [ $# -ne 2 ]
   3  then
   4        echo "Insira dois números para comparação"
   5        echo "Uso: ./compara_numeros.sh num1 num2"
   6        exit
   7  elif [ $1 -eq $2 ]
   8  then
   9        echo "$1 e igual a $2"
  10  elif [ $1 -lt $2 ]
  11  then
  12        echo "$1 e menor que $2"
  13  else
  14        echo "$1 e maior que $2"
  15  fi 

Após editar o script, deve-se salvar e dar permissão para execução utilizando o comando chmod:

$ chmod +xcompara_numeros.sh

Este script tem por objetivo comparar dois valores, sendo que, na análise do código, o if testa inicialmente se o usuário passou a quantidade de parâmetros necessária para realizar a comparação entre dois valores utilizando a variável especial $#. Caso não tenha os parâmetros necessários, é exibida uma mensagem de como executar o programa e o script é encerrado, conforme apresentado no trecho do código a seguir:

2 if [ $# -ne 2 ]

3 then

4 echo "Insira dois números para comparação"

5 echo "Uso: ./compara_numeros.sh num1 num2"

6 exit

Nesse script, para efeito de comparação numérica, foi utilizado o operador NotEqual (-ne), isto é, se a quantidade de parâmetros que o usuário passou não for igual a 2, a mensagem de alerta é exibida e o script é encerrado. A Tabela 3 apresenta os operadores que podem ser utilizados para comparação numérica.

Tabela 3. Operadores utilizados em testes de comparação numérica.

Prosseguindo com a análise do código do script compara.sh, se o usuário passou a quantidade de parâmetros correta, o bloco elif é executado.

Nesse bloco de código, os números digitados são comparados por meio dos operadores -eq (EQual) e –lt (LessThan), respectivamente, exibindo uma mensagem ao usuário. Por fim, se nenhum dos blocos mencionados corresponderem aos números que o usuário digitou, o bloco else será executado exibindo a mensagem de que o primeiro número digitado é maior que o segundo, conforme mostra o trecho do código:

13 else

14 echo "$1 e maior que $2"

15 fi

A Listagem 18 apresenta o script compara_numeros.sh em execução.

Listagem 18. Script compara_números. sh em execução.


Script executado sem os parâmetros:
  [aluno@lab: ~/scripts]$ ./compara_numeros.sh 
  Insira dois números para comparação
  Uso: ./compara_numeros.sh num1 num2
   
  Script executando comparações de números:
  [aluno@lab: ~/scripts]$ ./compara_numeros.sh 7 7
  7 e igual a 7
   
  [aluno@lab: ~/scripts]$ ./compara_numeros.sh 5 9
  5 e menor que 9 
  [aluno@lab: ~/scripts]$ ./compara_numeros.sh 2 17
  2 e menor que 17 
  [aluno@lab: ~/scripts]$ ./compara_numeros.sh 17 2
  17 e maior que 2

case

O comando case executa um bloco de código de acordo com o valor de uma variável, sendo recomendado seu uso quando a quantidade de comandos condicionais (if) sucessivos for maior que três. Essa prática permite uma execução ágil, aumenta a legibilidade do código, bem como diminui o seu tamanho. Normalmente o case é utilizado para construção de interfaces de menu que tomarão alguma decisão baseada em várias opções de entrada do usuário. A Listagem 19 apresenta a sintaxe do comando case.

Listagem 19. Sintaxe estrutura condicional case.


case <valor> in
      opcao1)
  bloco de comandos 
          ;;
      opcao2)
  bloco de comandos 
          ;;
      *)
          operação padrão
  esac 

Na sintaxe apresentada, <valor> é comparado de forma seletiva a cada um dos padrões (opcao1 e opcao2) até que um deles seja satisfeito, quando então passará a executar o bloco de comandos subsequentes até encontrar dois pontos-e-vírgulas sucessivos (;;) que indicam o fim do bloco de comandos.

Caso o valor informado não satisfaça nenhum dos padrões, será executado o bloco de comandos da entrada *) e o case será finalizado com o comando esac.

A Listagem 20 demonstra um exemplo de utilização da estrutura condicional case para uma calculadora de números inteiros, em que são combinados recursos como exibição de mensagem na tela, teste de parâmetros e operações aritméticas no shell.

Listagem 20. Exemplo de utilização da estrutura condicional case.


  1   #!/bin/bash
  2   echo "Calculadora no shell"
  3   if [ $# -ne 3 ]
  4   then
  5         echo "Uso: ./calculadora.sh num1 num2 operacao"
  6         echo "As operações possíveis são: soma subtrai multiplica e divide"
  7   else
  8         case "$3" in
  9              soma)
  10                echo "Soma: $1 + $2 = $(expr $1 + $2)"
  11                ;;
  12             subtrai)
  13                echo "Subtração: $1 - $2 = $(expr $1 - $2)"
  14                ;;
  15             multiplica)
  16                echo "Multiplicação: $1 * $2 = $(expr $1 \* $2)"
  17                ;;
  18             divide)
  19                echo "Divisão: $1 / $2 = $(expr $1 \/ $2)"
  20                ;;
  21             *)
  22                echo "Digite dois números inteiros e escolha uma das operações abaixo:"
  23                echo "soma subtrai multiplica e divide"
  24                ;;
  25        esac
  26  fi

O script apresentado tem por objetivo executar operações aritméticas por meio de valores passados via linha de comando e o tipo de operação que deseja realizar. O comando case analisa o conteúdo das variáveis $1 e $2 (primeiro e segundo parâmetros passado para o programa, respectivamente) e compara se o valor da variável $3 coincide com as opções que possui. Se a variável $3, por exemplo, for soma, o bloco de comandos referente à soma será executado e assim por diante.

A opção *) é padrão no case. Deste modo, caso o parâmetro passado não seja igual a nenhuma das outras opções anteriores, esse bloco de comandos será executado.

Estruturas de Repetição

Todo programa tem uma entrada de dados, processamento e saída, sendo que muitas vezes o processamento é feito em ciclos, que normalmente são chamados de laços. As estruturas de repetição ou laços são utilizadas para repetir um bloco de comandos por uma quantidade de vezes determinada por um limite que é definido no script na própria estrutura de repetição.

for

O laço for utiliza uma lista na qual os valores são atribuídos um a um a uma variável de controle, sendo que a cada atribuição de valor à variável de controle o bloco de comandos do é executado. A Listagem 21 apresenta a sintaxe desse comando.

Listagem 21. Sintaxe do comando for.


for <var> in <list>
  do
  Bloco de comandos
  done 

Na sintaxe apresentada, a cada repetição o for associa um item da lista <list> à variável <var> e executa os comandos até que toda a lista seja percorrida. A Listagem 22 apresenta um exemplo de uso deste comando, listando o conteúdo do diretório corrente.

Listagem 22. Exemplo de uso do comando for.


$ vim lista.sh 
  1   #!/bin/bash
  2   ARQ=$(ls)
  3   echo "Lista de arquivos do diretório $(pwd):"
  4   for nomes in $ARQ;
  5   do
  6         echo $nomes
  7   done 

No exemplo, o resultado do comando ls é armazenado na variável chamada ARQ, e o comando echo exibe a mensagem “Lista de arquivos do diretório $(pwd)”, sendo que a variável $(pwd) será convertida para o path absoluto (caminho completo) do diretório onde o usuário se encontra no momento da execução do programa, conforme é mostrado no trecho a seguir:

2 ARQ=$(ls)

3 echo "Lista de arquivos do diretório $(pwd):"

Em seguida o comando for é executado, e para cada arquivo que o laço lê, o nome é armazenado na variável nomes. Logo após é executado o bloco do, onde o nome do arquivo será mostrado na tela por meio do comando echo (linha 6). Esse processo será repetido até que o último nome de arquivo encontrado seja exibido. A construção do for é finalizada com o comando done, exibido na linha 7.

while

O laço while é uma estrutura de controle de fluxo que permite que um conjunto de instruções seja executado enquanto uma condição estiver satisfeita, ou seja, os comandos serão executados continuamente enquanto uma condição retornar um código 0 (verdadeira). A Listagem 23 apresenta a sintaxe do comando while.

Listagem 23. Sintaxe do comando while.


while<comando>
  do
  Bloco de comandos
  done 

Na sintaxe apresentada, o comando é executado e seu código de retorno é testado. Caso seja igual a 0, só então o bloco de comandos é executado, e ao encontrar o comando done, o ciclo é reiniciado até que o comando devolva um código de retorno diferente de 0, quando então a execução do programa é finalizada. A Listagem 24 demonstra um exemplo do comando while.

Listagem 24. Exemplo de uso do comando while.


#!/bin/bash
  echo "Digite o nome do mascote do linux: "
  read MASCOTE
  while [ $MASCOTE != "tux" ]
  do
  echo "Nome digitado esta errado"
  echo "Digite novamente o nome do mascote do linux: "
  read MASCOTE
  done
  echo "O nome esta correto!" 

Neste exemplo, o script lê o conteúdo da variável MASCOTE até que a resposta seja tux, ou seja, a pergunta será repetida enquanto a resposta for diferente de tux ($MASCOTE = "tux"). Quando a resposta for correta, a repetição é encerrada e o script será finalizado pelo comando done.

Conclusão

Neste artigo apresentamos os principais conceitos sobre shell script, um item essencial para usuários e administradores de sistemas. O shell script permite explorar e personalizar o ambiente e combinar comandos de forma a realizar tarefas que vão da criação de arquivos até a realização de backups.

Esta rica variação demonstra a importância de se dominar o foco desse estudo. Com ele é possível automatizar tarefas e tornar nossas atividades de administração de sistemas muito mais simples.

Referências

Robbins, A., Beebe, N. H. F. Classic Shell Scripting. O'Reilly. 2005.

Albing, C., Vossen, JP., Newham, C. Bash Cookbook - Solutions and Examples for bash Users. O'Reilly. 2007.

Lakshman, S. Linux Shell Scripting Cookbook. Packt Publishing. 2011.

Links

Bash Reference Manual
http://www.gnu.org/software/bash/manual/bashref.html#What-is-Bash_003f

COOPER, M. Advanced Bash-Scripting Guide
http://tldp.org/LDP/abs/html/index.html

Site Oficial KornShell
http://www.kornshell.com/

Canivete Suíço do Shell
http://aurelio.net/shell/canivete/

Evolução de shells no Linux
http://www.ibm.com/developerworks/br/library/l-linux-shells/

Shell builtin
http://en.wikipedia.org/wiki/Shell_builtin