Português do Brasil English
Devin no Facebook  Devin no Twitter  RSS do Site 
Programação    

Shell Script: tratamento de argumentos e opções


Comentários  8
Visualizações  
352,385

Na grande maioria das vezes, a linguagem shell script é usada para criar uma sequência de comandos que automatizam uma tarefa. Nisso, ela é extremamente eficiente e rápida. Combinar comandos é uma grande vantagem que o jeito UNIX de ser nos trouxe: várias ferramentas que fazem tarefas básicas, especializadas, e que quando se juntam realizam grandes feitos trabalhando juntas. Mas isso nunca impediu que se criasse também programas completos em shell script.

Uma característica para deixar um shell-script mais robusto e menos “sequencial/batch-mode” é o tratamento de argumentos. No meu clássico tutorial Programando em Shell-Script, o tópico Variáveis Especiais nos traz os primeiros itens que devemos aprender para o tratamento de argumentos. Existem variáveis especiais que tratam os argumentos passados para um programa ou uma função. Estes são:

  • $0 – Retorna o nome do script que foi executado
  • $N – Onde N é um número, corresponde ao argumento passado (1 = primeiro argumento, 2 = segundo argumento, 3 = terceiro argumento, etc)
  • $* – Retorna todos os argumentos de uma vez.
  • $# – Retorna a quantidade de argumentos passado para o script. (argc)

Vejamos agora um shell-script exempo que faz uso de todos esses argumentos:

#!/bin/bash

if [ $# -lt 1 ]; then
   echo "Faltou utilizar pelo menos um argumento!"
   exit 1
fi

echo "Numero de argumentos: $#"

COUNT=0
for ARG in $*; do
   COUNT=`expr $COUNT + 1`
   echo "Argumento $COUNT: $ARG"
done

As linhas 3 a 6 verificam se a quantidade de argumentos ($#) é menor (-lt – less than) que 1. Ou seja, se o usuário não chamou o programa com nenhum argumento, ele imprime um erro e sai do programa com status 1.

A linha 8 mostra quantos argumentos foram utilizados, usando novamente o $#.

O resto das linhas, 10 a 14, usam o $* com um laço for e um contador para mostrar quais foram os argumentos.

Executando agora este script sem argumentos:

$ ./tmp.sh
Faltou utilizar pelo menos um argumento!

Agora executando com dois argumentos:

$ ./tmp.sh naosei testando
Numero de argumentos: 2
Argumento 1: naosei
Argumento 2: testando

E agora com 4 argumentos:

$ ./tmp.sh a b c d
Numero de argumentos: 4
Argumento 1: a
Argumento 2: b
Argumento 3: c
Argumento 4: d

Bem simples né?

Argumentos como opções e seus valores

Algo comum que vemos nos programas são opções. Opções não deixam de ser argumentos para um programa, mas eles tem um significado especial. Do tipo: Se a opção -d existir, ativar durante o programa o modo de depuração. Se houver um -h, então mostre uma ajuda e não faça mais nada. Se houver um -v mostre a versão, e por aí vai.

Exemplo:

#!/bin/bash

case $1 in
   "-h") echo "Isto seria uma ajuda... Mas fiquei com preguiça de escrevê-la."
         ;;
   "-v") echo "Versão 666."
         ;;
   *) echo "Opção inválida!"
      exit 1
      ;;
esac

Exemplos do uso do script:

$ ./tmp.sh -h
Isto seria uma ajuda... Mas fiquei com preguiça de escrevela.

$ ./tmp.sh -v
Versão 666.

$ ./tmp.sh -O
Opção inválida!

$ ./tmp.sh
Opcao invalida!

Com isso a gente resolve um problema e cria mais outros dois…

  • E se o usuário colocar as duas opções? Só uma funcionaria.
  • E se uma das opções precisasse de um valor? Estilo “-f arquivo.log” gravaria um arquivo de log com as operações.

Poderíamos escrever vários algoritmos que verificassem cada um de todos os argumentos, testasse se fosse um ou outro, utilizasse as opções… Mas felizmente não precisamos fazer nada disso! O bash conta com uma função interna que trata os argumentos: o famoso getopts.

Utilizando o getopts para tratar tratar argumentos e opções

Seguindo a mesma linha de raciocínio, vamos logo para um exemplo de programa. Supondo que queiramos um shell-script que faça isso:

  • Caso a opção -h seja usada, mostra a ajuda e sai do programa.
  • Caso a opção -v seja usada, mostra a versão e sai do programa.
  • Caso a opção -o seja usada, grava um arquivo de log com as operações efetuadas e resultados.
  • Caso a opção -u seja usada, mostra o resultado do comando “uname -a”
  • Caso a opção -m seja usada, mostra o resultado do comando “free -m”
  • Caso a opção -s seja usada, mostra o resultado do comando “swap -s”

Note que apenas as opções -h e -v saem do programa após a execução. Agora vamos ao código:

#!/bin/bash

function PrintUsage() {
   echo "Uso: `basename $0` <-umsf> [-ohv]"
   exit 1
}

while getopts "hvo:umsf" OPTION
do
   case $OPTION in
      h) PrintUsage
         ;;
      v) echo "`basename $0` versao 666."
         exit
         ;;
      o) ARQUIVO_LOG=$OPTARG
         ;;
      u) DO_UNAME=1
         ;;
      m) DO_FREE=1
         ;;
      s) DO_SWAPON=1
         ;;
      ?) PrintUsage
         ;;
   esac
done
shift $((OPTIND-1))

if [ -z "$DO_UNAME" ] && [ -z "$DO_FREE" ] && [ -z "$DO_SWAPON" ] && [ -z "$DO_FDISK" ]; then
   PrintUsage
fi

if [ "$ARQUIVO_LOG" ]; then   echo "Execucao iniciada em `date`." >> $ARQUIVO_LOG

   if [ "$DO_UNAME" == 1 ]; then
      uname -a >> $ARQUIVO_LOG
   fi

   if [ "$DO_FREE" == 1 ]; then
      free -m >> $ARQUIVO_LOG
   fi

   if [ "$DO_SWAPON" == 1 ]; then
      swapon -s >> $ARQUIVO_LOG
   fi
else
   echo "Execucao iniciada em `date`."
   if [ "$DO_UNAME" == 1 ]; then
      uname -a
   fi

   if [ "$DO_FREE" == 1 ]; then
      free -m
   fi

   if [ "$DO_SWAPON" == 1 ]; then
      swapon -s
   fi
fi

O interessante para nós são as linhas 8 a 28. O laço while getopts começa a tratar todos os argumentos. A cada iteração do laço, ele coloca a letra da opção na variável $OPTION.

Note que para cada opção que precisamos, colocamos uma letra no primeiro argumento do getopts:

while getopts "hvo:umsf" OPTION

Note também que depois da letra o temos um dois pontos (:). Esse dois pontos significa que logo após a opção -o, o usuário precisa fornecer um valor. Este valor é automaticamente armazenado na variável $OPTARG.

Dessa maneira, podemos executar esse programa de diversas formas:

./tmp.sh -o arquivo.log -u
(executa o "uname -a" e grava no arquivo arquivo.log)

./tmp.sh -um
(executa os comandos "uname -a" e "free -m")

./tmp.sh -m -s -u
(executa os comandos "free -m", "swapon -s" e "uname -a")

Ou seja, não importa a ordem, o getopts vai reconhecer e executar as ações de acordo com a opção especificada.

E se você colocar uma opção que não está contemplatada… O “?” do case irá ser executado, por exemplo:

$ ./tmp.sh -a
./tmp.sh: illegal option -- a
Uso: tmp.sh <-umsf> [-ohv]

E dessa forma fica bem fácil de entender e usar o getopts :) Depois que o laço é todo feito e executado em todos os argumentos (no meu caso, preferi apenas configurar variáveis para cada opção e tratá-las depois), ele executa o comando que está na linha 28:

shift $((OPTIND-1))

Este comando faz com que os argumentos de opções sejam “comidos“, até que não sobre nenhuma opção. Em outras palavras, os argumentos representados pelas variáveis $N só serão aqueles que não pertençam a nenhuma opção. Exemplo:

./tmp.sh -u -o arquivo.log -m argumento1 argumento2

Nesse caso, o $1 seria o argumento1 e o $2 seria o argumento2, quando na verdade, sem o shift, eles seriam respectivamente o $5 e $6.

Como nem tudo é perfeito, a função getopts do bash não aceita opções longas (–nome-da-opcao), ou seja, voce só pode utilizar uma letra como opção. Represente bem suas opções com as letras! :)

Argumentos dentro de funções

Se dentro de um shell-script temos uma função, essa função é enxergada pela shell como se fosse um comando. Nesse sentido, dentro de uma função as variáveis $N definidas pelo programa não funcionarão. Exemplo:

#!/bin/bash

function Dummy() {
   echo "Numero de argumentos: $#"

   COUNT=0
   for ARG in $*; do
      COUNT=`expr $COUNT + 1`
      echo "Argumento $COUNT: $ARG"
   done
}

Dummy

Não importa o que você executar com o script acima, a saída será sempre a mesma: 0 números de argumentos, como mostrado a seguir.

$ ./tmp.sh
Numero de argumentos: 0

$ ./tmp.sh naosei temporario
Numero de argumentos: 0

$ ./tmp.sh a b c d e f g
Numero de argumentos: 0

Para a função Dummy, as variáveis especiais dos argumentos funcionam apenas para a função e não para o programa inteiro. É como se as variáveis fossem locais, e não globais. Vamos então substituir a linha da chamada da função Dummy (linha 13) por:

Dummy a b c d

E tentar executar novamente:

$ ./tmp.sh
Numero de argumentos: 4
Argumento 1: a
Argumento 2: b
Argumento 3: c
Argumento 4: d

Sabendo disso, não se percam na hora de usar os argumentos dentro das funções e lembrem-se que isto pode ser útil na hora de implementar diversas funções dentro de um script. Um bom exemplo disso é implementar a função PrintUsage que usamos anteriormente para, além de mostrar uma mensagem de uso, mostrar também uma mensagem de erro personalizada:

function PrintUsage() {
   [ "$1" ] && echo -ne "Erro: $1\n"
   echo "Uso: $(basename $0)  <-umsf> [-ohv]"
   exit 1
}

Agora é so chamar a função como…

PrintUsage "Faltando parâmetros."
PrintUsage "Opção inválida."
PrintUsage "No donut for you."

Use a criatividade de um programador (afinal, programação é arte) e comece a aprimorar suas ferramentas bash! :)


Comentários  8
Visualizações  
352,385


TagsLeia também

Apaixonado por Linux e administração de sistemas. Viciado em Internet, servidores, e em passar conhecimento. Idealizador do Devin, tem como meta aprender e ensinar muito Linux, o que ele vem fazendo desde 1997 :-)


Leia também



Comentários

8 respostas para “Shell Script: tratamento de argumentos e opções”

  1. Victor Farias disse:

    cara sempre tive essa duvida, vlw

  2. Fabio Parada disse:

    Gostaria de saber qual editor voce usa nos scripts, gostei muito da tela com os exemplos acima, se der me passe o nome do Editor e onde baixar.

    Parabens pelo site .

    Abraco

    Fabio Parada

  3. Eduardo Colabardini disse:

    Muito interessante esse tratamento de argumentos com o getopts, realmente amazing. Obrigado.

  4. felipe disse:

    excelente guia,ajudou-me Muito!obrigado!

  5. [...] Vejamos agora um shell-script exempo que faz uso de todos esses argumentos: view source print? [...]

  6. no painel do meu site tem esse Shell Script

    Tem como eu usar ele para tirar Backup dos e-email do meu site

    tipo sempre tenho que trocar a hospedagem de varios site tirando backup dos e-mail 1 a 1

    tem como fazer isso automaticamente por esse painel Shell que tem no CPANEL

    ou eu to falando Besteira e viajei

  7. charlene disse:

    Tem como eu usar como entrada um programa em c por exembro (soma.c) e fazer que ela seja compilada pelo gcc atravez de um script…ele manda esse programa ser compilado pelo gcc e mostra o resultado

Deixe uma resposta