Criando programas com opções

Publicado por Leandro Santiago em 06/08/2007

[ Hits: 10.610 ]

Blog: http://leandrosan.wordpress.com

 


Criando programas com opções



Esta dica não aborda como criar programas que recebem parâmetros. O foco é como interpretar tais parâmetros. Argumentos da linha de comando, se assim desejar.

Há vários métodos de se fazer isto. Uma maneira das mais simples é usando o comando eval. Se há um comando que eu tenho certeza de que que foi criado por Chuck Norris, é o eval!

Um exemplo. Quero fazer um programa que receba como parâmetros três arquivos. O conteúdo dos arquivos ArqA e ArqB serão concatenados, gerando o arquivo ArqC. Você então poderia criar um programa que fosse usado da seguinte maneira:

$ ./progama1 Arquivo01.txt Arquivo02.txt ArquivoFinal.txt

Ele seria muito simples:

#!/bin/bash
# programa1
cat $1 $2 > $3

Mas se, por exemplo, que quisesse que o arquivo final fosse aquele recebido como primeiro parâmetro? Teria que modificar todo o script? Eis então que entra em cena o eval.

Cria-se um programa com o seguinte conteúdo:

#!/bin/bash
# programa2
eval $@
cat $ArqA $ArqB > $ArqC

E seu uso seria o seguinte:

$ ./programa1 ArqC=ArquivoFinal.txt ArqA=Arquivo01.txt ArqB=Arquivo02.txt

Assim não importa a ordem dos parâmetros! Você não entendeu nada? O eval simplesmente executaria as várias atribuições que são passadas por parâmetro. Continuou não entendendo? Então leia esta dica:
Mas esta maneira tem algumas falhas e não é tão legal para o usuário final, certo? Eis que surge, das entranhas do GNU, o utilitário getopts, que permite a criação de programas que recebem opções curtas. O que são opções curtas? Segundo o Aurélio, Opções curtas tem somente uma letra. Exemplo: -c -v

Opções --longas: Tem mais de uma letra. Exemplo: --enable-menu --help

Um uso do getopts é este:

#!/bin/bash
#programa3
while getopts "hVm:" opt;
do
   case $opt in
      h) echo Help;;
      V) echo Version ;;
      m) echo $OPTARG ;;
      *) echo Qualquer coisa menos o desejado ;;
   esac
done

Basicamente é isto: o argumento do getopts são as letras que servirão como opções, seguido da variável que receberá, a cada momento, as estas opções. $OPTARG guarda o argumento que acompanha a opção.

Usando:

$ ./programa3 -h -m "mensagem qualquer"
Help
mensagem qualquer

Ou:

$ ./programa3 -hm "mensagem qualquer"
Help
mensagem qualquer

$ ./programa3 -hm "mensagem qualquer" -j
Help
mensagem qualquer
./programa3: illegal option -- j
Qualquer coisa menos o desejado

Ou seja, este comando também verifica erros. Mas tem a desvantagem de não permitir opções longas, nem mensagens de erro personalizadas. Uma pena.

Mas, como estamos falando de shell-script, sempre há uma última alternativa, conhecida popularmente como "fazer na mão".

Essa implementação vai de pessoa para pessoa. Eu vou passar aqui a minha. Faço isso por acreditar que este método é bastante portável, e você não encontrará dificuldade em adaptá-la em outros programas.

Ela tem a seguinte cara:

#!/bin/bash
# programa4
# Função que exibe uma tela de ajuda

ExibirAjuda()
{
   echo "uso: parametros.sh -x número -y número"
}

# Função que exibe uma tela com a versão
ExibirVersion()
{
   echo -e "\tversão 0.1ALPHA"
}

# Função que recebe um parâmetro
# e retorna 0 se ele é um número inteiro. E 1, caso contrário
EhNumero()
{
   ( [ $1 -ne 0 ] || [ $1 -e 0 ] ) &> /dev/null && return 0 || return 1
}

# Função que verifica os parâmetros
FiltraParam()
{
   # vetor que guarda todos os parâmetros
   PARAMET=($@);
   # Índice verificado
   INDICE=0
   # Indica se devo parar a verificação
   FIMCADEIA=false
   # Alguma opção que pode ser ativada/desativada (você pode ter quantas quiser!)
   DEBUG=false
   # A quantidade de parâmetros que devo verificar (poderia pegar o tamanho do vetor, mas assim também funciona)
   QtdPar=$#
   # Enquanto houver parâmetros para verificar
   # e não estiver
   while ((INDICE<QtdPar)) && ( ! $FIMCADEIA )
   do
      case ${PARAMET[$INDICE]} in
         -x|-y|-z|--paramx|--paramy|--paramz)
         if ((INDICE<QtdPar-1))
         then
            ((INDICE++))

            # Aqui verifico parâmetros que necessitam de argumento
            # juntei tudo num case para facilitar a localização
            # e modificação
            case ${PARAMET[((INDICE-1))]} in
               -x|--paramx) PARAMX=${PARAMET[$INDICE]}
               ;;
               -y|--paramy) PARAMY=${PARAMET[$INDICE]}
               ;;
               -z|--paramz) PARAMZ=${PARAMET[$INDICE]}
               ;;
               *) echo "${PARAMET[((INDICE-1))]} precisa de um argumento válido"
                  FIMCADEIA=true
               ;;
            esac
         fi
         ;;

         # Parâmetro que finaliza a verificação dos parâmetros
         -h|--help)
            ExibirAjuda
            FIMCADEIA=true
         ;;
         # Parâmetro que finaliza a verificação dos parâmetros
         -v|--version)
            ExibirVersion
            FIMCADEIA=true
         ;;
         # Parâmetro que não necessita de argumento
         --enable-debug) DEBUG=true
         ;;
         # Outro que também não precisa
         --disable-debug) DEBUG=false
         ;;
         *) echo "Parâmetro invalido: ${PARAMET[((INDICE))]} no índice $INDICE"
            FIMCADEIA=true
         ;;
      esac
      ((INDICE++))
   done
  
}

# Inicializo as variáveis, com qualquer coisa que não seja número
  
PARAMY=default
PARAMX=default
PARAMZ=default
  
if [ $# -eq 0 ]
then
   ExibirAjuda
   exit 1
else
   FiltraParam $@
   if ! EhNumero $PARAMY
   then
      echo "-y precisa de um parâmetro valido, que não seja $PARAMY"
   fi
   if ! EhNumero $PARAMX
   then
      echo "-x precisa de um parâmetro valido, que não seja $PARAMX"
   fi
   if ! EhNumero $PARAMZ
   then
      echo "-z precisa de um parâmetro válido, que não seja $PARAMZ"
   fi
   if $DEBUG
   then
      echo "Agora eu vou executar algo em ${PARAMX} X ${PARAMY} X ${PARAMZ} com o debug habilitado"
   else
      echo "Agora eu vou executar algo em ${PARAMX} X ${PARAMY} X ${PARAMZ} com o debug desabilitado"
   fi
fi

E seu uso é o seguinte:

$ ./programa4 -x 60 --enable-debug -x 90 -z 35
$ ./programa4 --help

etc

Execute ele várias vezes, mudando a ordem das opções, para confirmar.

Ele fará todo o "vuco-vuco" necessário, dentro da função FiltraParam() e no final definirá o conteúdo das variáveis. Estas variáveis são globais, deixo claro. Basicamente porque esta função só é executada uma vez no programa.

A passagem de parâmetros (no caso, argumentos digitados pelo usuário) se dá da maneira mais tosca que alguém pode fazer. Isso significa que parâmetros com espaço, mesmo que entre aspas, atrapalham a execução da função. Se quiser uma maneira mais eficiente, leia estes meus outros textos:
É importante notar que a "magia" do programa está na função FiltraParam(). O resto do programa é somente para comprovar que a coisa funciona. Faça os testes aí e comprove. Eu "agarantiu". ;-)

Outra coisa: como disse, essa foi a maneira que eu implementei a solução. Você é livre para fazer do jeito que bem desejar.

Bom pessoal, espero ter contribuído com a comunidade. Se este texto lhe servir de ajuda, eu me sentirei satisfeito. Deixe bons comentários também. Tô precisando alimentar meu ego. :-)

Obrigado.

Outras dicas deste autor

Escolhendo a faixa de áudio e legenda nos vídeos em formato OGM

Desligando o seu computador com o dedão do pé

Acessando mais que 9 parâmetros em shell script

Como preparar a compilação de um programa que utiliza Qt4

Usando udev alternativo no Slackware 11

Leitura recomendada

Shell script - Variáveis especiais

Conhecendo o printf

Shell Script nosso de cada dia - Episódio 1

C Dialog

Aulas Shell Script do zero - Parte 8

  

Comentários
[1] Comentário enviado por tenchi em 06/08/2007 - 11:47h

Oops, citei o Aurélio, mas me esqueci de citar a fonte.
Para quem não conhece, o Aurélio (Verde) é uma das maiores Otoridades em shell-script do Brasil.
http://www.aurelio.net/
http://aurelio.wordpress.com/

Outro site muito bom é o do Julio Neves:
http://www.julioneves.com/

Pronto! Já fiz o meu Jabá ;-)






Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts