Numa dica anterior eu havia apresentado uma solução para criar programas que recebessem parâmetros na linha-de-comando, e que soubessem interpretar os valores recebidos.
O link para o texto original é:
Agora mostro o antigo método, mas melhorado. Agora ele é capaz de resolver parâmetros que utilizam mais de um argumento.
Mostro aqui um script de exemplo, que utiliza o método q criei:
#!/bin/bash
# Um exemplo de tratamento de parâmetros
# recebidos pela linha-de-comando no bash
#
# Por Leandro Santiago da Silva
# leandrosansilva [ ARROBA ] gmail [PONTO] com
# Inicializo variáveis booleanas
PARAMD=false
PARAME=false
# Crio o vetor que guarda todos os parâmetros
j=0
for i
{
PARAMET[$((j++))]="$i"
}
# Índice verificado
INDICE=0
# Indica se devo parar a verificação
FIMCADEIA=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 acabado a lista
while (( INDICE<QtdPar )) && ( ! $FIMCADEIA )
do
case ${PARAMET[$INDICE]} in # Opções que necessitam de argumentos -A|--param-a|-B|--param-b|-C|--param-c)
# Aqui verifico parâmetros que necessitam de argumento.
# Juntei tudo num case para facilitar a localização
# e modificação
if ((INDICE<QtdPar))
then
((INDICE++))
if ((${#PARAMET[((INDICE))]}>0))
then
#Área 1
case ${PARAMET[((INDICE-1))]} in
-A|--param-a) PARAMA=${PARAMET[((INDICE))]}
;;
-B|--param-b) PARAMB=${PARAMET[((INDICE))]}
;;
-C|--param-c) PARAMC=${PARAMET[((INDICE))]}
;;
esac
else
echo ${PARAMET[((INDICE-1))]} precisa de um argumento
FIMCADEIA=true
fi
fi
;;
# Parâmetros que recebem mais que um argumento
-K|--param-k|-J|--param-j)
INDICE_PARAM=$INDICE
while ((INDICE<QtdPar)) && ( ! [ "${PARAMET[((INDICE+1))]:0:1}" = "-" ] )
do
#Área 2
case ${PARAMET[$INDICE_PARAM]} in
-K|--param-k) PARAMK="$PARAMK ${PARAMET[((INDICE+1))]}"
;;
-J|--param-j) PARAMJ="$PARAMJ ${PARAMET[((INDICE+1))]}"
;;
esac
((INDICE++))
done
;;
#Área 3
-D|--param-d) PARAMD=true
;;
-E|--param-e) PARAME=true
;;
--help) echo "Uso:
--param-a|-A opção
--param-b|-B opção
--param-c|-C opção
--param-d|-D (Ativa)
--param-e|-E (Ativa)
--param-k|-K mais de uma opção
--param-j|-J mais de uma opção"
exit
;;
# Se o parâmetro for incorreto, paro a verificação
*) echo Parâmetro inválido: ${PARAMET[((INDICE))]}
FIMCADEIA=true
;;
esac
((INDICE++))
# Se passei um parâmetro errado, imediatamente finalizo o programa
if $FIMCADEIA
then
exit 4 ## Porquê 4?
fi
done
# Relatório final
echo PARAMA = $PARAMA
echo PARAMB = $PARAMB
echo PARAMC = $PARAMC
echo PARAMK = $PARAMK
echo PARAMJ = $PARAMJ
if $PARAMD
then
echo PARAMD está ativo
else
echo PARAMD está inativo
fi
if $PARAME
then
echo PARAME está ativo
else
echo PARAME está inativo
fi
Agora, explico o que cada coisa faz.
Primeiramente inicializo as variáveis booleanas. Isto é opcional, já que nem todos os programas as utilizam.
Logo após, crio um vetor com os parâmetros. Aqui, cada opção recebida pela linha-de-comando, tenha caractere espaço ou não, é colocada num elemento.
Alguns me perguntariam: "Mas porque colocar tudo num vetor?"
Eu diria: "Já vi muitas implementações de interpretação de parâmetros que manipulavam cada opção avançando com o comando shift. E confesso que não gostei nenhum pouco."
Com vetores você manipula somente índices. Modificando o valor do índice, você tem acesso à qualquer elemento do vetor na hora q bem desejar. Pode voltar ou avançar da maneira que quiser.
Isto tem um lado bom e um ruim. O bom é que é a lógica é - em minha opinião - mais fácil de ser visualizada. Outra vantagem é que você pode colocar as ações a serem executadas dentro do local correto, e não precisar se preocupar com a lógica da verificação.
Vejam como exemplo o caso dos parâmetros que recebem uma só opção. No caso do tratamento dos parâmetros '-A' e '--param-a', bastou o código PARAMA=${PARAMET[((INDICE))]} para que o próximo elemento fosse considerado um argumento dos parâmetros em questão. Não precisei analisar mais nada. Só uma linha-de-código.
Mas talvez o maior problema seja o tamanho do código. Ele pode parecer grande quando temos poucas opções à tratar, mas parece menor quando as opções são muitas.
Na "Área 1" você pode colocar os parâmetros que recebem somente um parâmetro. Como é um case, basta colocar o nome do parâmetro e o comando que deve ser executado com o argumento dele.
Em "Área 2", você coloca os parâmetros que recebem os parâmetros que podem receber um ou mais parâmetros. O engraçado é que ele elimina a utilidade da verificação acima. Mas tem o problema de utilizar um critério de parada diferente. Ele entende como "a partir daqui já outra opção" quando o próximo elemento do vetor começa com um hífen (-), enquanto que o método anterior não faz isso. Aí fica à seu critério se utiliza o primeiro ou não.
Já em "Área 3" você coloca as opções que não recebem argumento.
Agora mostro um exemplo de uso. Salvo o exemplo acima no arquivo teste-parametros.sh e dou permissão de execução à ele.
$ ./teste-parametros.sh -E --param-k um dois três -A quatro -J cinco seis -K sete oito -B nove
PARAMA = quatro
PARAMB = nove
PARAMC =
PARAMK = um dois três sete oito
PARAMJ = cinco seis
PARAMD está inativo
PARAME está ativo
Agora percebam este outro exemplo, onde não coloco argumento algum com um parâmetro que pode receber mais que um deles:
$ ./teste-parametros.sh --param-k -A 20
PARAMA = 20
PARAMB =
PARAMC =
PARAMK =
PARAMJ =
PARAMD está inativo
PARAME está inativo
E comparo com este outro:
$ ./teste-parametros.sh -A --param-j um dois três
Parâmetro inválido: um
Neste caso, vejam que "--param-j" ficou como argumento de "-A", e "um" foi reconhecido como um outro parâmetro, no caso, inválido.
Para verificar isso, executamos:
$ ./teste-parametros.sh -A --param-j
PARAMA = --param-j
PARAMB =
PARAMC =
PARAMK =
PARAMJ =
PARAMD está inativo
PARAME está inativo
O ideal é que você não coloque nenhuma ação para ser executada no momento da verificação dos parâmetros.O ideal é somente definir flags q servirão para uso do programa.
Deixo claro aqui que este exemplo, bem como o código e a lógica do script são de livre uso. Mas só peço que, se for utilizar este sistema em algum script, que cite meu nome e e-mail ;-)
Esclareço também que este código não é definitivo. Ele certamente tem falhas, e não há nada que impeça que você o melhore. Mas, se fizer isto, tente ao máximo retribuir com a comunidade mostrando as melhorias que criou.