Usando As Funções getopt() e getopt_long() Em C

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 */

Download: prog1.c(.docx)

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 != NULLgetopt_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 */

Download prog2.c(.docx)

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 */

Download myrm.c(.docx)

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

6 pensamentos sobre “Usando As Funções getopt() e getopt_long() Em C

  1. 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.

  2. 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!

Deixe um comentário