Executando verificação de segurança...
7

[Tutorial] Parseando argumentos em C

Temos várias maneiras de fazer isso, e aqui estarei abordando duas delas, somente com bibliotecas nativas de Unix/Linux.

Maneira rudimentar

Esse é o clássico:

main.c

#include <stdio.h>

int main(int argc, char *argv[]) {
  /* O nome do programa conta como um argumento, e é o primeiro,
   * sendo assim, precisamos pôr esse if para que não seja nulo.
  */
  if(argc < 2) {
    printf("Você precisa especificar um argumento!");
    return -1;
  }
  printf("Olá, %s!\n", argv[1]); // argv[0] = nome do programa
  return 0;
}

Podemos compilar e rodar: gcc main.c -o main e ./main "Seu nome aqui"

⚠️ A partir daqui, estarei assumindo que você vai compilar os programas sozinho.

getopt

getopt é usado pra parsear opções na linha de comando, foi feito para parsear de forma simples e checar por opções válidas. Ele usa o GNU getopt.

Funções e argumentos

Todas as funções do getopt retornam inteiros.

int optionIndex = 0 // O getopt_long pede, pois vamos usar num while loop.

// Modo simples, não suportando argumentos longos.
getopt(argc, argv, "optstring, veremos a frente."); 
// Modo longo, suportando tanto argumentos simples quanto longos.
getopt_long(argc, argv, "optstring", ArrayDeStructComArgumentosLongos, &optionIndex)
// Modo long, mas argumentos com apenas `-` não são tratados como argumentos.
getopt_long_only(argc, argv, "optstring", ArrayDeStructComArgumentosLongos, &optionIndex)

O que é esse tal optstring?

É uma string, todos os caracteres dessa string representam uma opção, e se essa opção precisar de um argumento, essa opção precisa estar seguida de um :

Por exemplo, "hvl:", -he-vnão tem argumentos, mas-l` sim.

O struct do modo longo

Ele precisa ter 4 valores:

  • nome
  • se tem um argumento
    (no_argument ou 0 caso não tenha, required_argument ou 1 caso seja um argumento obrigatório, e optional_argument ou 2, caso seja opcional)
  • flag
  • val
    (seu equivalente em modo simples)

E na verdade, é um array de structs.

#include <getopt.h>

static struct option longopts[] = {
  // nome, argumento,   flag, val
  {  "help", no_argument, 0,  'h' },
  // Obrigatoriamente, o ultimo "argumento" tem que ser apenas zeros.
  { 0, 0, 0, 0 }
};

Juntando tudo que você aprendeu

Nós já sabemos quais funções temos, sabemos usar o optstring, sabemos "configurar" o struct, só falta uma coisa: optarg, mas é muito simples: ele é simplesmente o retorno da opção, se ela tiver um. então se eu rodar ./main -a arroz, optarg seria arroz.

#include <stdio.h>
#include <getopt.h>

void help(char *command_name){
  printf("Usage: %s [arguments]\n", command_name);
}

int main(int argc, char *argv[]) {
  // Retorno do getopt + index.
  int opt, longIndex;
  static struct option longopts[] = {
    {"version", no_argument, 0, 'v'},
    {"help", no_argument, 0, 'h'},
    {0,0,0,0}
  };

  /* Se a opção for encontrada, getopt retorna a opção
   * Se todas as opções já foram parseadas, retorna -1
   * Se encotrar uma opção invalida, retorna ?
   * Que pode ser usado pelo "default:"
  */
  while((opt = getopt_long(argc, argv, "hv", longopts, &longIndex)) != -1) {
    switch(opt) {
    case 'h': // Caso a opção seja h ou help.
      help("testando");
      break;
    case 'v': // Caso a opção seja v ou version.
      printf("Claramente 0.0.0.0.0\n");
      break;
    default: // Caso a opção seja invalida
      help("testando");
      break;
    }
    
  }
}

❤️ você chegou até aqui! Se tiver alguma dúvida, por favor deixe-me saber, tentarei respondê-la.

Fontes

Carregando publicação patrocinada...
1

Tu também pode fazer utilizando o laço for e ficaria assim


#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]){
    
    // Começamos com int = 1 para não contar com o nome do programa
    for (int i = 1; i < argc; i++){
        if (strcmp(argv[i],{Nome da flag desejada}) == 0){
            // Chama função da flag
        }
    }
    
    return 0;
}

Este seria um método mais Anti-GNU que poderia ser usado. Mas ele provavelmente deixaria seu código um pouco mais gordinho do que se ele tivesse feito a dieta do GNU

2