Por que C++ é a linguagem ideal para utilitários CLI?
Quando se trata de criar utilitários CLI em C++, muitos desenvolvedores encolhem os ombros: "Por quê? Existem Python, Go, Rust". Mas vamos encarar a realidade. Se você precisa de velocidade relâmpago, consumo mínimo de memória e controle total sobre o hardware — C++ está fora de competição. Administradores de sistemas, engenheiros DevOps e desenvolvedores de sistemas embarcados ainda escrevem suas ferramentas de console nessa linguagem. E sabe de uma coisa? Isso é correto.
Hoje vamos abordar a criação de um utilitário CLI em C++ do zero — desde o parsing de argumentos até a compilação do binário. Você aprenderá a escrever uma ferramenta real e útil que não terá vergonha de publicar no GitHub. Vamos lá.
Ferramentas: o que vamos precisar
Para trabalhar confortavelmente, precisaremos de:
- Compilador C++17/20 — GCC (MinGW no Windows) ou Clang. Pessoalmente, recomendo GCC 11+.
- Editor de texto ou IDE — VS Code com plugin C++, CLion, ou até Vim (para os hardcore).
- CMake — para compilação facilitada. Embora seja possível usar Makefile, CMake é o padrão moderno.
- Git — não podemos ficar sem controle de versão.
Também precisaremos de uma biblioteca para parsing de argumentos de linha de comando. Não vamos reinventar a roda e usaremos a lendária getopt (disponível em sistemas POSIX) ou escreveremos nosso próprio parser minimalista. A segunda opção é ainda mais interessante — você entenderá como funciona "por baixo dos panos".
Estrutura do projeto
Antes de escrever código, vamos decidir o que exatamente estamos criando. Que seja o utilitário fileutils — uma ferramenta de console para trabalhar com arquivos. Ele será capaz de:
- Contar o número de linhas, palavras e caracteres em um arquivo (análogo ao
wc). - Buscar uma string por padrão (análogo ao
grepem versão mínima). - Exibir ajuda (
--help).
Estrutura de pastas:
fileutils/├── src/│ ├── main.cpp│ ├── parser.cpp│ └── parser.h├── CMakeLists.txt└── README.mdPasso 1: Parser de argumentos próprio
Vamos escrever um parser simples, mas funcional. Ele processará flags longas (--lines, --words) e argumentos posicionais (nome do arquivo).
Arquivo parser.h
#ifndef PARSER_H#define PARSER_H
#include <string>#include <vector>#include <map>
struct Args { bool show_lines = false; bool show_words = false; bool show_chars = false; std::string pattern; std::vector<std::string> files;};
Args parse_args(int argc, char* argv[]);
#endifArquivo parser.cpp
#include "parser.h"#include <iostream>
Args parse_args(int argc, char* argv[]) { Args args; for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; if (arg == "--help" || arg == "-h") { std::cout << "Uso: fileutils [OPÇÕES] ARQUIVO..." << "Opções:" << " --lines, -l Exibir contagem de linhas" << " --words, -w Exibir contagem de palavras" << " --chars, -c Exibir contagem de caracteres" << " --search PAT Buscar por padrão" << " --help, -h Mostrar esta ajuda"; exit(0); } else if (arg == "--lines" || arg == "-l") { args.show_lines = true; } else if (arg == "--words" || arg == "-w") { args.show_words = true; } else if (arg == "--chars" || arg == "-c") { args.show_chars = true; } else if (arg == "--search" && i + 1 < argc) { args.pattern = argv[++i];