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/bashif [ $# -lt 1 ]; then echo "Faltou utilizar pelo menos um argumento!" exit 1fiecho "Numero de argumentos: $#"COUNT=0for 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.shFaltou utilizar pelo menos um argumento! |
$ ./tmp.sh naosei testandoNumero de argumentos: 2Argumento 1: naoseiArgumento 2: testando |
$ ./tmp.sh a b c dNumero de argumentos: 4Argumento 1: aArgumento 2: bArgumento 3: cArgumento 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/bashcase $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 -hIsto seria uma ajuda... Mas fiquei com preguiça de escrevela.$ ./tmp.sh -vVersão 666.$ ./tmp.sh -OOpção inválida!$ ./tmp.shOpcao 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/bashfunction PrintUsage() { echo "Uso: `basename $0` <-umsf> [-ohv]" exit 1}while getopts "hvo:umsf" OPTIONdo 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 ;; esacdoneshift $((OPTIND-1))if [ -z "$DO_UNAME" ] && [ -z "$DO_FREE" ] && [ -z "$DO_SWAPON" ] && [ -z "$DO_FDISK" ]; then PrintUsagefiif [ "$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 fielse 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 fifi |
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 -- aUso: 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/bashfunction Dummy() { echo "Numero de argumentos: $#" COUNT=0 for ARG in $*; do COUNT=`expr $COUNT + 1` echo "Argumento $COUNT: $ARG" done}Dummy |
$ ./tmp.shNumero de argumentos: 0$ ./tmp.sh naosei temporarioNumero de argumentos: 0$ ./tmp.sh a b c d e f gNumero de argumentos: 0 |
1
| Dummy a b c d |
$ ./tmp.shNumero de argumentos: 4Argumento 1: aArgumento 2: bArgumento 3: cArgumento 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/