Uma característica para deixar um shell-script mais robusto e menos “sequencial/batch-mode” é o tratamento de argumentos. No meu clássico tutorial Programando em Shell-Script, o tópico Variáveis Especiais nos traz os primeiros itens que devemos aprender para o tratamento de argumentos. Existem variáveis especiais que tratam os argumentos passados para um programa ou uma função. Estes são:
- $0 – Retorna o nome do script que foi executado
- $N – Onde N é um número, corresponde ao argumento passado (1 = primeiro argumento, 2 = segundo argumento, 3 = terceiro argumento, etc)
- $* – Retorna todos os argumentos de uma vez.
- $# – Retorna a quantidade de argumentos passado para o script. (argc)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
| #!/bin/bash if [ $ # -lt 1 ]; then echo "Faltou utilizar pelo menos um argumento!" exit 1 fi echo "Numero de argumentos: $#" COUNT=0 for ARG in $*; do COUNT=` expr $COUNT + 1` echo "Argumento $COUNT: $ARG" done |
A linha 8 mostra quantos argumentos foram utilizados, usando novamente o $#.
O resto das linhas, 10 a 14, usam o $* com um laço for e um contador para mostrar quais foram os argumentos.
Executando agora este script sem argumentos:
$ ./tmp.sh Faltou utilizar pelo menos um argumento! |
$ ./tmp.sh naosei testando Numero de argumentos: 2 Argumento 1: naosei Argumento 2: testando |
$ ./tmp.sh a b c d Numero de argumentos: 4 Argumento 1: a Argumento 2: b Argumento 3: c Argumento 4: d |
Argumentos como opções e seus valores
Algo comum que vemos nos programas são opções. Opções não deixam de ser argumentos para um programa, mas eles tem um significado especial. Do tipo: Se a opção -d existir, ativar durante o programa o modo de depuração. Se houver um -h, então mostre uma ajuda e não faça mais nada. Se houver um -v mostre a versão, e por aí vai.Exemplo:
01
02
03
04
05
06
07
08
09
10
11
| #!/bin/bash case $1 in "-h" ) echo "Isto seria uma ajuda... Mas fiquei com preguiça de escrevê-la." ;; "-v" ) echo "Versão 666." ;; *) echo "Opção inválida!" exit 1 ;; esac |
$ ./tmp.sh -h Isto seria uma ajuda... Mas fiquei com preguiça de escrevela. $ ./tmp.sh -v Versão 666. $ ./tmp.sh -O Opção inválida! $ ./tmp.sh Opcao invalida! |
- E se o usuário colocar as duas opções? Só uma funcionaria.
- E se uma das opções precisasse de um valor? Estilo “-f arquivo.log” gravaria um arquivo de log com as operações.
Utilizando o getopts para tratar tratar argumentos e opções
Seguindo a mesma linha de raciocínio, vamos logo para um exemplo de programa. Supondo que queiramos um shell-script que faça isso:- Caso a opção -h seja usada, mostra a ajuda e sai do programa.
- Caso a opção -v seja usada, mostra a versão e sai do programa.
- Caso a opção -o seja usada, grava um arquivo de log com as operações efetuadas e resultados.
- Caso a opção -u seja usada, mostra o resultado do comando “uname -a”
- Caso a opção -m seja usada, mostra o resultado do comando “free -m”
- Caso a opção -s seja usada, mostra o resultado do comando “swap -s”
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
| #!/bin/bash function PrintUsage() { echo "Uso: `basename $0` <-umsf> [-ohv]" exit 1 } while getopts "hvo:umsf" OPTION do case $OPTION in h) PrintUsage ;; v ) echo "`basename $0` versao 666." exit ;; o) ARQUIVO_LOG=$OPTARG ;; u) DO_UNAME=1 ;; m) DO_FREE=1 ;; s) DO_SWAPON=1 ;; ?) PrintUsage ;; esac done shift $((OPTIND-1)) if [ -z "$DO_UNAME" ] && [ -z "$DO_FREE" ] && [ -z "$DO_SWAPON" ] && [ -z "$DO_FDISK" ]; then PrintUsage fi if [ "$ARQUIVO_LOG" ]; then echo "Execucao iniciada em `date`." >> $ARQUIVO_LOG if [ "$DO_UNAME" == 1 ]; then uname -a >> $ARQUIVO_LOG fi if [ "$DO_FREE" == 1 ]; then free -m >> $ARQUIVO_LOG fi if [ "$DO_SWAPON" == 1 ]; then swapon -s >> $ARQUIVO_LOG fi else echo "Execucao iniciada em `date`." if [ "$DO_UNAME" == 1 ]; then uname -a fi if [ "$DO_FREE" == 1 ]; then free -m fi if [ "$DO_SWAPON" == 1 ]; then swapon -s fi fi |
Note que para cada opção que precisamos, colocamos uma letra no primeiro argumento do getopts:
1
| while getopts "hvo:umsf" OPTION |
Dessa maneira, podemos executar esse programa de diversas formas:
./tmp.sh -o arquivo.log -u (executa o "uname -a" e grava no arquivo arquivo.log) ./tmp.sh -um (executa os comandos "uname -a" e "free -m") ./tmp.sh -m -s -u (executa os comandos "free -m", "swapon -s" e "uname -a") |
E se você colocar uma opção que não está contemplatada… O “?” do case irá ser executado, por exemplo:
$ ./tmp.sh -a ./tmp.sh: illegal option -- a Uso: tmp.sh <-umsf> [-ohv] |
1
| shift $((OPTIND-1)) |
./tmp.sh -u -o arquivo.log -m argumento1 argumento2 |
Como nem tudo é perfeito, a função getopts do bash não aceita opções longas (–nome-da-opcao), ou seja, voce só pode utilizar uma letra como opção. Represente bem suas opções com as letras! :)
Argumentos dentro de funções
Se dentro de um shell-script temos uma função, essa função é enxergada pela shell como se fosse um comando. Nesse sentido, dentro de uma função as variáveis $N definidas pelo programa não funcionarão. Exemplo:
01
02
03
04
05
06
07
08
09
10
11
12
13
| #!/bin/bash function Dummy() { echo "Numero de argumentos: $#" COUNT=0 for ARG in $*; do COUNT=` expr $COUNT + 1` echo "Argumento $COUNT: $ARG" done } Dummy |
$ ./tmp.sh Numero de argumentos: 0 $ ./tmp.sh naosei temporario Numero de argumentos: 0 $ ./tmp.sh a b c d e f g Numero de argumentos: 0 |
1
| Dummy a b c d |
$ ./tmp.sh Numero de argumentos: 4 Argumento 1: a Argumento 2: b Argumento 3: c Argumento 4: d |
1
2
3
4
5
| function PrintUsage() { [ "$1" ] && echo - ne "Erro: $1\n" echo "Uso: $(basename $0) <-umsf> [-ohv]" exit 1 } |
PrintUsage "Faltando parâmetros." PrintUsage "Opção inválida." PrintUsage "No donut for you." |
Fonte: http://www.devin.com.br/shell-script-tratamento-de-argumentos-e-opcoes/