De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.
Colaboração: Júlio Cezar Neves
Data de Publicação: 17 de outubro de 2017
Analisar uma linha de comando e quebrá-la em cada uma de suas opções é uma
tarefa complicada, pois, só falando em duas opções (-A
e -B
) sendo que -B
pode ou não ter argumentos (ARG), teríamos de pesquisar por:
-AB | -ABARG | -AB ARG |
-A -B | -A -BARG | -a -B ARG |
-BA | ||
-B -A | -BARG -A | -B ARG -A |
Isso sem falar que, se uma das opções for facultativa, a gama da análise
(provavelmente em um comando case
) seria o dobro dessa. Em virtude dessa
complexidade e para aliviar a vida do programador, foi desenvolvido o nosso
amigo getopts
, que, por ser um intrínseco (builtin), é bastante veloz. Ele
foi feito para suceder a antiga implementação que chamava-se getopt
(sem o s
),
que não era intrínseco e tinha alguns bugs, mas seu único pecado é não aceitar
opções longas dos comandos, previstas pelo GNU (como em CMD --help
, p.ex.).
Só para melhorar o entendimento unificando a terminologia que será usada, pelos
parágrafos anteriores você já deve ter reparado que chamamos de opção uma
letra precedida de um sinal de menos ou traço (-
) e argumentos são os textos
requeridos por algumas opções ou simplesmente passados para a linha de comando.
Exemplo:
cut -f 2 -s -d : /etc/passwd
Trecho | Terminologia |
---|---|
cut | Programa |
-f -s -d | Opções |
2 | Argumento da opção -f |
: | Argumento da opção -d |
/etc/passwd | Argumento do programa |
Sintaxe:
getopts CADOPTS VAR [ARGS]
Onde:
CADOPTS |
contém a cadeia de opções e suas eventuais necessidades de argumento. Quando uma opção aceita um argumento, ela deverá vir seguida de um dois pontos (: ). Como veremos adiante esta variável receberá um ponto de interrogação (? ) em caso de erro. Assim sendo, o ponto de interrogação (? ) e os dois pontos (: ) são especiais para o getopts e em virtude disso esses dois caracteres não podem ser usados como opções. |
VAR |
O comando terá de ser executado (normalmente dentro de um loop de while ) tantas vezes quantas foram as opções válidas. A variável VAR , receberá em cada passada a opção (tirada de CADOPTS ) que será analisada, ou terá uma sinalização (flag) de erro. |
ARGS |
Normalmente são pesquisadas as opções passadas em VAR , mas se os argumentos forem passados em ARGS , eles é que serão analisados, ou seja, a existência desse parâmetro diz ao getopts para analisar o conteúdo de ARGS em vez dos parâmetros posicionais. |
Variáveis usadas pelo getopts:
OPTARG | Contém cada argumento das opções descobertas em cada volta do loop do getopts ou uma marca de erro que analisaremos à frente; |
OPTIND | Contém o índice do parâmetro posicional em análise que você gerenciará para saber, pelo valor de OPTIND a opção que está sendo trabalhada; |
OPTERR | É uma variável que quando tem valor 1 indica para Shell que os erros irão para a tela e este é o padrão pois é sempre inicializada com valor 1. Com valor zero, os erros são assinalados, mas não são exibidos. |
Como já vimos o getopts
pode ser executado de dois modos:
Modo silencioso | Quando a variável do sistema $OPTERR for zero ou quando a cadeia que você passou em OPTARG começa por dois pontos (: ); |
Modo falador | Este é o normal. OPTERR é igual a um e a cadeia passada em OPTARG não começa por dois pontos (: ). |
Se for achada uma opção inválida, getopts
põe um ponto de interrogação (?
) em
VAR
. Se não estiver em modo silencioso dá uma mensagem de erro e esvazia
OPTARG
. Se estiver silencioso, o caractere que gerou a opção inválida é
colocado em OPTARG
.
Se um argumento requerido não for informado e getopts
não estiver em modo
silencioso, um ponto de interrogação (?
) é colocado em VAR
, OPTARG
é esvaziada
e é dada uma mensagem de erro. Caso esteja em modo silencioso, um dois pontos
(:
) é colocado em VAR
e OPTARG
recebe o caractere da opção da qual não foi
informado o argumento requerido
Quando o assunto é programação em bash, aconselho que, em seus scripts, sempre use o modo silencioso e monitore eventuais erros.
Como esse cara funciona?
Vamos fazer essa análise por exemplos, que é muito mais simples de aprender:
Exemplo:
Suponha que um programa possa receber a opção -C
. O script, para análise
das opções deveria ser do tipo:
$ cat cut1.sh
#!/bin/bash
while getopts c Opc
do
case $Opc in
c) echo Recebi a opção -c
;;
esac
done
Como vamos trabalhar diversos exemplos em cima deste mesmo script, preferi
já começá-lo com um case, quando nesse caso bastaria um if
.
Vamos vê-lo funcionando:
1. Executando-o sem nenhuma opção, ele não produz nenhuma saída;
2. Usando a opção -c, que é o esperado:
$ cut1.sh -c
Recebi a opção -c
3. Usando a opção -z
, que é inválida:
$ cut1.sh -z
./cut1.sh: opção ilegal -- z
Xiii, quem deu a mensagem foi o Shell e eu não soube de nada. Mesmo com erro, o programa continuaria em execução, porque o erro não é tratado pelo programa.
Então vamos alterá-lo colocando-o em modo silencioso, o que, como já vimos,
pode ser feito iniciando a cadeia de opções (CADOPTS
) com um dois pontos
ou atribuindo zero à variável $OPTERR
.
$ cat cut2.sh
#!/bin/bash
while getopts :c Opc
do
case $Opc in
c) echo Recebi a opção -c
;;
\?) echo Caractere $OPTARG inválido
exit 1
done
Agora coloquei um dois pontos à frente de CADOPTS
e passei a monitorar um
ponto de interrogação (?
) no case
, porque quando é achado um caractere
inválido, o getopts
, como já vimos, coloca um ponto de interrogação (?
)
na variável $Opc
. Desta forma, listei $OPTPARG
e em seguida dei exit
.
Note que antes da interrogação coloquei uma contrabarra (\
), para que o
Shell não o expandisse para todos os arquivos que contém somente um caractere
no nome.
4. Agora que já tenho o ambiente sob meu controle, vamos executá-lo novamente com uma opção não prevista:
$ cut2.sh -z
Caractere z inválido
$ echo $?
1
Agora aconteceu o que queríamos: deu a nossa mensagem e o programa abortou,
passando 1
como código de retorno ($?
).
Bem, já examinamos todas as possibilidades que a passagem de opções
pode ter. Vamos agora esmiuçar o caso que uma opção que tenha parâmetro
associado. Para dizer que uma opção pode ter um argumento, basta colocar um
dois pontos (:
) após a letra da opção.
Então, ainda evoluindo o programa de teste que estamos fazendo vamos supor que a opção -c requeresse um argumento. Deveríamos então fazer algo como:
$ cat cut3.sh
#!/bin/bash
while getopts :c: Opc
do
case $Opc in
c) echo Recebi a opção -c
echo Parâmetro passado para a opção -c foi $OPTARG
;;
\?) echo Caractere $OPTARG inválido
exit 1
;;
:) echo -c precisa de um argumento
exit 1
esac
done
Agora introduzimos o caractere dois pontos (:
) no case porque, como já
vimos, quando um parâmetro não é localizado, o getopts
em modo silencioso
coloca um dois pontos (:
) em $Opc
, caso contrário, a falta de argumento
é sinalizada, com um ponto de interrogação (?
) nesta mesma variável.
Vamos então analisar todas as possibilidades:
1. Executando-o sem nenhuma opção, ele não produz nenhuma saída;
2. Passando a opção -c
acompanhada de seu parâmetro, que é o esperado:
$ cut3.sh -c 2-5
Recebi a opção -c
Parâmetro passado para a opção -c foi 2-5
3. Passando a opção correta, porém omitindo o parâmetro requerido:
$ cut3.sh -c -c precisa de um argumento $ echo $? 1
Para finalizar esta série, vejamos um caso interessante. Desde o início,
venho simulando nesse exemplo a sintaxe do comando cut
com a opção
-c
. Para ficar igualzinho à sintaxe do cut
, só falta receber o nome
do arquivo. Vejamos como fazê-lo:
$ cat cut4.sh
#!/bin/bash
# Inicializar OPTIND é necessário caso o script tenha
#+ usado getopts antes. OPTIND mantém seu valor
OPTIND=1
while getopts :c: Opc
do
case $Opc in
c) echo Recebi a opção -c
echo Parâmetro passado para a opção -c foi $OPTARG
;;
\?) echo Caractere $OPTARG inválido
exit 1
;;
:) echo -c precisa de um argumento
exit 1
esac
shift $((--OPTIND))
Args="$@"
echo "Recebi o(s) seguinte(s) argumento(s) extra(s): $Args"
done
E executando-o vem:
$ cut4.sh -c 2-5 /caminho/do/arquivo
Recebi a opção -c
Parâmetro passado para a opção -c foi 2-5
Recebi o(s) seguinte(s) argumento(s) extra(s): /caminho/do/arquivo
Agora vamos dar um mergulho num exemplo um bem completo e analisá-lo. Para esse
script interessam somente as opções -f
, -u
argumento e -C
. Veja
o código (mas este ainda não está 100%).
#!/bin/bash printf "%29s%10s%10s%10s%10s\n" Comentário Passada Char OPTARG OPTIND while getopts ":fu:C" VAR do case $VAR in f) Coment="Achei a opção -f" ;; u) Coment="Achei a opção -u $OPTARG" ;; C) Coment="Achei a opção -C" ;; \?) Coment="Achei uma opção invalida -$OPTARG" ;; :) Coment="Faltou argumento da opção -u" esac printf "%30s%10s%10s%10s%10s\n" "$Coment" $((++i)) "$VAR" "$OPTARG" "$OPTIND" done
Agora vejamos a sua execução passando todos as opções juntas e sem passar o
parâmetro requerido pela opção -u
(repare os dois pontos (:
) que seguem
o u
na chamada do getopts
neste exemplo).
$ getop.sh -fCxu
Comentário Passada Char OPTARG OPTIND
Achei a opção -f 1 f 1
Achei a opção -C 2 C 1
Achei uma opção invalida -x 3 ? x 1
Faltou argumento da opção -u 4 : u 2
Repare que a variável $OPTIND não foi incrementada. Vejamos então o mesmo exemplo, porém passando as opções separadas:
$ getop.sh -f -C -x -u
Comentário Passada Char OPTARG OPTIND
Achei a opção -f 1 f 2
Achei a opção -C 2 C 3
Achei uma opção invalida -x 3 ? x 4
Faltou argumento da opção -u 4 : u 5
Ah, agora sim! $OPTIND
passou a ser incrementado. Para fechar, vejamos um
exemplo sem opção inválida e no qual passamos o parâmetro requerido pela
opção -u
:
$ getop.sh -f -C -u Util
Comentário Passada Char OPTARG OPTIND
Achei a opção -f 1 f 2
Achei a opção -C 2 C 3
Achei a opção -u Util 3 u Util 5
Algumas observações:
1. Vimos que mesmo após encontrar erro, o getopts
continuou analisando
as opções, isso se dá porque o que foi encontrado pode ser um parâmetro de
uma opção e não um erro;
2. Então como distinguir um erro de um parâmetro? Fácil: se a opção em análise requer argumento, o conteúdo de $OPTARG é ele, caso contrário é um erro;
3. Quando encontramos um erro devemos encerrar o programa pois o getopts
só aborta sua execução quando:
-
);
O parâmetro especial --
marca o fim das opções, mas isso é uma convenção
para todos os comandos que interagem com o Shell.
Então a nossa versão final do programa seria:
$ cat getop.sh
#!/bin/bash
function Uso
{
echo " $Coment
Uso: $0 -f -C -u parâmetro" >&2
exit 1
}
(($#==0)) && { Coment="Faltou parâmetro"; Uso; }
printf "%29s%10s%10s%10s%10s\n" Comentário Passada Char OPTARG OPTIND
while getopts ":fu:C" VAR
do
case $VAR in
f) Coment="Achei a opção -f"
;;
u) Coment="Achei a opção -u $OPTARG"
;;
C) Coment="Achei a opção -C"
;;
\?) Coment="Achei uma opção invalida -$OPTARG"
Uso
;;
:) Coment="Faltou argumento da opção -u"
esac
printf "%30s%10s%10s%10s%10s\n" "$Coment" $((++i)) "$VAR" \
"$OPTARG" "$OPTIND"
done
Júlio Cézar Neves |
|
---|---|
O 4º UNIX do mundo nasceu na Cidade Maravilhosa, mais precisamente na Cobra Computadores, onde à época trabalhava o Julio. Foi paixão à 1ª vista! Desde então, (1980) atua nessa área como especialista em Sistemas Operacionais e linguagens de programação. E foi por essa afinidade que quando surgiu o Linux foi um dos primeiros a estudá-lo com profundidade e adotá-lo como Sistema Operacional e filosofia de vida. É autor dos livros Programação Shell Linux, 11ª edição e Bombando o Shell. |
This policy contains information about your privacy. By posting, you are declaring that you understand this policy:
This policy is subject to change at any time and without notice.
These terms and conditions contain rules about posting comments. By submitting a comment, you are declaring that you agree with these rules:
Failure to comply with these rules may result in being banned from submitting further comments.
These terms and conditions are subject to change at any time and without notice.
Comentários