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.