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

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s