Por que Go é a linguagem ideal para utilitários CLI?
\n\nImagine: você precisa escrever uma ferramenta que será executada no console, processar arquivos, acessar APIs ou simplesmente automatizar tarefas rotineiras. Você pode escolher Python, Node.js ou Bash. Mas se velocidade, tamanho do binário e facilidade de distribuição são críticos — sua escolha são utilitários CLI em Go.
\n\nGo compila em um único binário estático. Sem dependências, sem "você tem Python 3.8 instalado?". Baixou — executou. Isso torna as ferramentas CLI Golang o padrão de facto no mundo DevOps (lembre-se de Docker, Kubernetes, Hugo — todos escritos em Go).
\n\nNeste artigo, não vamos apenas falar sobre teoria. Vamos criar do zero um utilitário de console em Go real, que fará parsing de JSON, trabalhará com HTTP e exibirá tabelas bonitas. Você aprenderá como estruturar o projeto, tratar erros e compilar para qualquer plataforma.
\n\nConfiguração do ambiente e estrutura do projeto
\n\nAntes de escrever código, certifique-se de ter o Go instalado (versão 1.21+). Verifique facilmente:
\n\ngo version\n\nVamos criar um diretório para o nosso projeto. Nomearemos o utilitário de json2table — ele lerá JSON do stdin ou de um arquivo e exibirá os dados em formato de tabela.
mkdir json2table\ncd json2table\ngo mod init github.com/seu-username/json2table\n\nA estrutura do projeto será clássica para desenvolvimento em Go:
\n\n- \n
main.go— ponto de entrada \n internal/— lógica interna (parsing, saída) \n cmd/— código para tratamento de comandos (se usar Cobra) \n
Começaremos de forma simples, sem frameworks, e depois adicionaremos flags e ajuda.
\n\nEscrevendo o núcleo do utilitário: parsing de JSON e saída de tabela
\n\nCrie o arquivo internal/parser.go. Aqui vamos analisar JSON e transformá-lo em uma estrutura.
package internal\n\nimport (\n "encoding/json"\n "fmt"\n "io"\n)\n\n// Record representa uma linha da tabela\ntype Record map[string]interface{}\n\n// ParseJSON lê JSON de um reader e retorna um slice de registros\nfunc ParseJSON(r io.Reader) ([]Record, error) {\n var data []Record\n decoder := json.NewDecoder(r)\n if err := decoder.Decode(&data); err != nil {\n return nil, fmt.Errorf("erro ao fazer parsing do JSON: %w", err)\n }\n return data, nil\n}\n\nAgora crie internal/table.go para saída formatada:
package internal\n\nimport (\n "fmt"\n "strings"\n "text/tabwriter"\n "os"\n)\n\n// PrintTable exibe os registros em formato de tabela com alinhamento\nfunc PrintTable(records []Record) {\n if len(records) == 0 {\n fmt.Println("Nenhum dado para exibir")\n return\n }\n\n // Coleta todas as chaves únicas (cabeçalhos das colunas)\n headers := make([]string, 0)\n seen := make(map[string]bool)\n for _, rec := range records {\n for key := range rec {\n if !seen[key] {\n headers = append(headers, key)\n seen[key] = true\n }\n }\n }\n\n // Usa tabwriter para uma saída bonita\n w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)\n\n // Cabeçalho\n fmt.Fprintln(w, strings.Join(headers, "\\t"))\n\n // Separador\n sep := make([]string, len(headers))\n for i, h := range headers {\n sep[i] = strings.Repeat("-", len(h))\n }\n fmt.Fprintln(w, strings.Join(sep, "\\t"))\n\n // Dados\n for _, rec := range records {\n row := make([]string, len(headers))\n for i, h := range headers {\n if val, ok := rec[h]; ok {\n row[i] = fmt.Sprintf("%v", val)\n } else {\n row[i] = ""\n }\n }\n fmt.Fprintln(w, strings.Join(row, "\\t"))\n }\n\n w.Flush()\n}\n\n