Introdução
Essas duas funções são responsáveis por tratar as opções da linha de comando de programas escritos em C. Nesse post iremos aprender como usá-las de forma simples e prática.
Opções e Argumentos
Muitos se confundem em relação ao uso desses dois termos. As definições usadas pelos programas GNU/Linux são:
- Opções: são entradas do programa iniciadas por – (um menos) ou — (dois menos), elas geralmente indicam alguma ação ou estado.
- Argumentos: são entradas para opções e se referem ao valor de uma dada opção.
Em relação as opções temos:
Opções curtas
Opções curtas são aquelas definidas por somente um caractere precedido por ‘-‘. Elas podem ser usadas separadamente ou juntas [1]. Assim:
$ rm -i -f ~/meu_arquivo
faz o mesmo que:
$ rm -if ~/meu_arquivo
Opções longas
São aquelas definidas por strings precedidas por ‘–‘ (dois sinais de menos).
$ rm --force ~/meu_arquivo
Uma exceção acontece na função getopt_long_only() que aceita opções longas usando somente um sinal de menos ‘-‘. Um exemplo clássico é o comando find que aceita opções do tipo “-name”, “-exec”, etc.
Opções flags
São opções que não aceitam argumento e que representam um estado dentro do programa.
Um exemplo disso é a opção -i do programa rm [1].
Opções não flags
São as opções que aceitam argumentos.
Agora sobre os argumentos:
Argumentos com caracteres seguros
São strings cujos caracteres não são interpretados pelo shell.
Argumentos com caracteres não seguros
Esses argumentos contém caracteres que são interpretados pelo shell e devem ser escapados. Alguns desses caracteres não seguros são: $, <, >.
Argumentos únicos
São argumentos que não têm o caracter espaço.
Argumentos compostos
São argumentos que têm pelo menos um caracter espaço e por isso devem ser rodeados por aspas (simples ou duplas) para indicar um argumento único.
Para mais definições veja o link [1].
A função getopt()
Essa função é usada para tratar somente opções curtas. O protótipo dela é:
// $ man 3 getopt int getopt(int argc, char * const argv[], const char *optstring) ;
Parâmetros:
- argc: é o argc declarado em main().
- argv: é o argv declarado em main().
- optstring: define quais serão as opções aceitas pelo seu programa. Cada caractere dessa string é uma opção e aqueles caracteres seguidos por ‘:’ serão opções que aceitam argumentos.
Retorno:
- `caractere’: se uma opção válida é encontrada na linha de comando, o seu caractere correspondente é retornado.
- ?: se uma opção inválida é encontrada, ou seja, seu caractere não se encontra em optstring, então um ‘?’ é retornado. O interrogação é retornado também quando uma opção não flag é encontrada sem seu devido argumento.
- -1: indica que todas as opções já foram avaliadas.
Programa exemplo usando getopt()
/* * [prog1.c] * Programa que exemplifica o uso da função * getopt(). * * [Autor] * Marcos Paulo Ferreira (Daemonio) * undefinido gmail com * daemoniolabs.wordpress.com * * Ter Out 4 22:11:32 BRT 2011 */ #include <stdio.h> #include <stdlib.h> /* para getopt() */ #include <unistd.h> /* Mostra a ajuda */ void show_help(char *name) { fprintf(stderr, "\ [uso] %s <opcoes>\n\ -h mostra essa tela e sai.\n\ -n NOME seta o seu nome.\n\ -i IDADE seta sua idade.\n\ -e ESTADO seta seu estado.\n\ -c cadastra no banco de dados.\n", name) ; exit(-1) ; } int main(int argc, char **argv) { int opt ; /* Variáveis que receberão os argumentos * das opções. */ char *nome=NULL, *idade=NULL, *estado=NULL ; char flag_cadastrar = 0 ; /* Chama ajuda. */ if ( argc < 2 ) show_help(argv[0]) ; /* getopt() retorna o caractere de uma opção a cada * iteração e -1 para marcar o fim do processo. */ while( (opt = getopt(argc, argv, "hn:i:e:c")) > 0 ) { switch ( opt ) { case 'h': /* help */ show_help(argv[0]) ; break ; case 'n': /* opção -n */ nome = optarg ; break ; case 'i': /* opção -i */ idade = optarg ; break ; case 'e': /* opção -e */ estado = optarg ; break ; case 'c': /* opção -c */ flag_cadastrar = 1 ; break ; default: fprintf(stderr, "Opcao invalida ou faltando argumento: `%c'\n", optopt) ; return -1 ; } } /* Mostra os dados na tela. */ printf("Seus dados: \n\ Nome : %s\n\ Idade : %s\n\ Estado : %s\n", nome, idade, estado) ; printf("Dados gravados no banco de dados: ") ; if ( flag_cadastrar == 0 ) printf("NAO.\n") ; else printf("SIM.\n") ; /* Mostra os argumentos em excesso */ if ( argv[optind] != NULL ) { int i ; puts("** Argumentos em excesso **") ; for(i=optind; argv[i] != NULL; i++) { printf("-- %s\n", argv[i]) ; } } return 0 ; } /* EOF */
Compilando e usando
$ gcc -o prog1 prog1.c $ ./prog1 [uso] ./prog1 -h mostra essa tela e sai. -n NOME seta o seu nome. -i IDADE seta sua idade. -e ESTADO seta seu estado. -c cadastra no banco de dados. $ ./prog1 -n 'daemonio' Seus dados: Nome : daemonio Idade : (null) Estado : (null) Dados gravados no banco de dados: NAO. $ ./prog1 -n 'silvo santos' -i 99 -e rj Seus dados: Nome : silvo santos Idade : 99 Estado : rj Dados gravados no banco de dados: NAO.
Usando a opção -c indicamos para o programa “salvar” os dados no banco de dados:
$ ./prog1 -c -n 'silvo santos' -i 99 -e rj Seus dados: Nome : silvo santos Idade : 99 Estado : rj Dados gravados no banco de dados: SIM.
Olhando o código fonte do prog1.c vemos que algumas variáveis globais como optstring e optopt não foram declaradas no fonte, mas mesmo assim estão disponíveis para serem usadas. Essas variáveis são declaradas em <unistd.h> e seus usos e definições serão mostrados a seguir.
Variáveis globais disponíveis
- optarg: quando uma opção que recebe argumento é encontrada na linha de comando, o seu argumento é acessível pelo ponteiro optarg. Seu uso pode ser visto em prog1.c dentro do switch.
- optopt: sabemos que, quando uma opção inválida ou faltando argumento é encontrada, o valor ‘?’ é retornado. A variável optopt é setada com o caractere da opção que disparou o erro.
- optind: essa variável é um índice que aponta para o primeiro argumento em excesso na linha de comando. Argumentos em excessos são entradas para nenhuma opção. A função getopt() modifica a linha de comando quando ela trata as opções e todos os argumentos em excesso são enviados para o fim de *argv[] e o primeiro desses argumentos é indexado por optind, e o último sempre é o NULL. Se não há argumentos em excesso, a expressão argv[optind] retorna NULL.
- opterr: Quando usamos uma opção inválida, uma string de erro é mostrada na tela. Mensagens de erro são mostradas por getopt() se o valor dessa variável é diferente de zero. Setando o valor dessa variável para zero, nenhuma mensagem de erro será mostrada.
A função getopt_long()
Essa função além de aceitar opções curtas, como getopt(), aceita também opções longas e por esse motivo que ela é usada com mais frequência por incorporar ambos formatos de opções.
O protótipo dessa função, seguindo a man page, é:
// $ man 3 getopt_long int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex) ;
Os 3 primeiros parâmetros são idênticos àqueles de getopt(). O quarto é:
- struct option *longopts: enquanto as opções curtas são passadas em optstrings, as longas são passadas em forma de estrutura. A estrutura option é definida em <getopt.h>da seguinte maneira:
struct option { const char *name ; int has_arg ; int flag ; int val ; } ;
Explicando cada campo:
- name: é um ponteiro para o nome da opção.
has_arg: é um flag que tem três estados. São eles:
0 ou no_argument = opção não recebe um argumento.
1 ou required_argument = opção deve receber um argumento.
2 ou optional_argument = opção pode ou não receber um argumento (argumento opcional). - flag: define como os valores são retornados pela função. Se flag é NULL (o usual), então o valor de val é retornado por getopt_long(). Se flag != NULL, getopt_long() retorna zero e o valor de val é setado pelo endereço apontado por flag.
- val: é o valor retornado por getopt_long() quando uma opção longa em questão é encotrada. Geralmente o valor dessa variável é a mesma que a opção curta definida em optstring.
Por fim, o último argumento da função getopt_long():
- longindex: indica um endereço que receberá o índice da opção longa que está sendo processada pela função naquele momento.
A função getopt_long() retorna o caractere da opção curta quando ela é encontrada. Para uma opção longa, o valor de val é retornado se flag é NULL, caso contrário ela retorna zero e seta o valor de val no endereço apontado por flag. Os retornos -1 e ‘?’ têm o mesmo significados que em getopt().
Como construir as opções
Cada opção é uma estrutura do tipo option. Se um programa tem três opções, então três estruturas precisarão ser criadas. Em geral, cria-se um vetor de estruturas para armazenar todas as opções. O tamanho desse vetor é de n + 1, sendo n o total de opções e o +1 por causa do último elemento nulo que marca o fim do vetor.
Por exemplo, se um programa recebe as seguintes opções:
--nome NOME --idade IDADE --estado ESTADO --cadastrar
O vetor de estruturas option será:
const struct option stopcoes[] = { {"nome" , required_argument , 0 , 'n'}, {"idade" , required_argument , 0 , 'i'}, {"estado" , required_argument , 0 , 'e'}, {"cadastrar" , no_argument , &flag_cadastrar , 1 }, {0 , 0 , 0 , 0 }, } ;
Veja que o último elemento deve ter obrigatoriamente todos os seus campos setados como NULL (zero) para que função saiba o fim do vetor.
Programa exemplo para a função getopt_long()
/* * [prog2.c] * Programa que exemplifica o uso da função * getopt_long(). * * [Autor] * Marcos Paulo Ferreira (Daemonio) * undefinido gmail com * daemoniolabs.wordpress.com * * Ter Out 4 23:12:49 BRT 2011 */ #include <stdio.h> #include <stdlib.h> /* para getopt_long() */ #include <getopt.h> void show_help(char *name) { fprintf(stderr, "\ [uso] %s <opcoes>\n\ -h, --help mostra essa tela e sai.\n\ -n, --nome=NOME seta o seu nome.\n\ -i, --idade=IDADE seta sua idade.\n\ -e, --estado=ESTADO seta seu estado.\n\ -c, --cadastrar cadastra no banco de dados.\n", name) ; exit(-1) ; } int main(int argc, char **argv) { int opt ; /* Variáveis que receberão os argumentos * das opções. */ char *nome=NULL, *idade=NULL, *estado=NULL ; int flag_cadastrar = 0 ; /* Estrutura de opcoes. Veja que o último * elemento deve ser NULL. */ const struct option stopcoes[] = { {"help" , no_argument , 0 , 'h'}, {"nome" , required_argument , 0 , 'n'}, {"idade" , required_argument , 0 , 'i'}, {"estado" , required_argument , 0 , 'e'}, {"cadastrar" , no_argument , &flag_cadastrar , 1 }, {0 , 0 , 0 , 0 }, } ; if ( argc < 2 ) show_help(argv[0]) ; while( (opt = getopt_long(argc, argv, "hn:i:e:c", stopcoes, NULL)) > 0 ) { switch ( opt ) { case 'h': /* -h ou --help */ show_help(argv[0]) ; break ; case 'n': /* -n ou --nome */ nome = optarg ; break ; case 'i': /* -i ou --idade */ idade = optarg ; break ; case 'e': /* -e ou --estado */ estado = optarg ; break ; case 'c': /* -c ou --cadastrar */ flag_cadastrar = 1 ; break ; default: fprintf(stderr, "Opcao invalida ou faltando argumento: `%c'\n", optopt) ; return -1 ; } } printf("Seus dados: \n\ Nome : %s\n\ Idade : %s\n\ Estado : %s\n", nome, idade, estado) ; printf("Dados gravados no banco de dados: ") ; if ( flag_cadastrar == 0 ) printf("NAO.\n") ; else printf("SIM.\n") ; /* Mostra os argumentos em excesso */ if ( argv[optind] != NULL ) { int i ; puts("** Argumentos em excesso **") ; for(i=optind; argv[i] != NULL; i++) { printf("-- %s\n", argv[i]) ; } } return 0 ; } /* EOF */
Exemplo final
O código fonte a seguir simula o comando rm. Ele utiliza os argumentos em excesso como nomes de arquivos a serem deletados. Além disso, ele recebe opções flags como -i e -f para mudar o comportamento de deleção (-i para modo interativo e -f sem interação).
/* * [myrm.c] * Programa que simula o comando rm para * exemplificar o uso da função getopt_long(). * * [Autor] * Marcos Paulo Ferreira (Daemonio) * undefinido gmail com * daemoniolabs.wordpress.com * * Qui Out 6 17:11:56 BRT 2011 */ #include <stdio.h> /* for exit() */ #include <stdlib.h> /* for getopt_long() */ #include <getopt.h> /* for stat() */ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> /* for unlink() */ #define INTERACTIVE 0 #define FORCE 1 void remover_arquivo(char *nome) { if ( unlink(nome) < 0) { fprintf(stderr, "Impossivel remover: `%s'.\n", nome) ; } else { printf("`%s' removido.\n", nome) ; } } /* Se stat() retornar erro, supomos que arquivo * não existe. */ int arquivo_existe(char *nome) { struct stat s ; /* true se arquivo existe. */ return stat(nome, &s) >= 0 ; } void show_help(char *nome) { fprintf(stderr, "[uso] %s <opcoes>\n\ -h, --help mostra essa tela e sai.\n\ -i, --interactive pergunta antes de deletar arquivo.\n\ -f, --force deleta arquivo sem avisar.\n", nome) ; exit(-1) ; } int main(int argc, char **argv) { /* Variável que recebe dois valores: * INTERACTIVE quando aparece a opção -i e * FORCE quando aparece a opção -f. O valor padrão * é FORCE. */ int flagforce=FORCE ; int opt ; /* Estrutura de opções. */ const struct option stopcoes[] = { {"help", no_argument, 0, 'h'}, {"interactive", no_argument, &flagforce, INTERACTIVE}, {"force", no_argument, &flagforce, FORCE}, {0, 0, 0, 0}, } ; if ( argc < 2 ) { fprintf(stderr,"%s : Digite --help para mais informacoes.\n", argv[0]) ; return -1 ; } while ( (opt=getopt_long(argc, argv, "hif", stopcoes, NULL)) > 0 ) { switch ( opt ) { case 'h': show_help(argv[0]) ; break ; case 'i': flagforce = INTERACTIVE ; break ; case 'f': flagforce = FORCE ; break ; } } /* Deleta os argumentos em excesso. */ while ( argv[optind] != NULL ) { /* confere se arquivo existe. */ if ( arquivo_existe(argv[optind]) ) { /* -i ou --interactive */ if ( flagforce == INTERACTIVE ) { printf("Remover arquivo `%s'? ", argv[optind]) ; if ( getchar() == 'y' ) { remover_arquivo(argv[optind]) ; } /* Retira o newline */ getchar() ; } else { /* -f ou --force */ remover_arquivo(argv[optind]) ; } } else { /* arquivo não existe. */ fprintf(stderr, "Arquivo nao existe `%s'.\n", argv[optind]) ; } /* próximo argumento */ optind++ ; } return 0 ; } /* EOF */
Referências
[1] A Funcao getopt() e a Linha de Comando, by Copper Wire (Acessado em: Outubro 2011)
http://unlimitedinc.wikispaces.com/file/view/TUTORIAL_GETOPT%28%29.txt
Para quem desejar, há um bom material aqui também:
http://muitosmundos.wordpress.com/2010/05/13/tratando-argumentos-passados-a-um-programa-em-c/
Daemonio,
t+
Post editado em: 7/08/2012
O campo has_arg, da estrutura que compõem os argumentos de getopt_long(), quando é definido como zero indica que o argumento da linha de comando não pode receber um valor?
Sim, parece que cometi um erro. Se o valor é zero o argumento NÃO recebe argumento. Nesse caso é melhor usar o nome “no_argument” do que o zero propriamente dito.
Vou editar a postagem. Valew.
Olá Marcos, muito obrigado pelo seu excelente artigo, estive pesquisando sobre esse assunto e acabei caindo no seu blog. Obrigado por dedicar seu tempo, seus artigos com certeza ajudam muitas pessoas.
Forte abraço!
Muito obrigado, amigo! Abraços