Criando programas com opções

Publicado por Leandro Santiago em 06/08/2007

[ Hits: 10.800 ]

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

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

Slim, um belo e leve gerenciador de login

Usando udev alternativo no Slackware 11

Ripando CD's de áudio no XMMS.

Obtendo CDs do Ubuntu no Brasil

Leitura recomendada

Como excluir um determinado kernel no Ubuntu

Goosh.org, um shell Google

Sintetizador de Voz Off-Line Masculino e Feminino

Script básico para ouvir MP3 aleatórias

Contar e ordenar a quantidade de ocorrências de cada linha em um arquivo

  

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