
Dès qu'un outil en ligne de commande dépasse une poignée d'options, gérer à la main le parsing des arguments, l'aide et les sous-commandes devient ingérable. Cobra est la bibliothèque qui structure tout cela : c'est elle qui propulse kubectl, docker ou gh. Ce guide construit un outil scan avec une commande racine, deux sous-commandes et des flags, puis montre l'aide et l'autocomplétion générées. Public visé : intermédiaire en Go. Tous les exemples sont testés avec Go 1.26 et Cobra 1.10.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Initialiser une commande racine Cobra et l'exécuter.
- Ajouter des sous-commandes et une arborescence.
- Distinguer flags persistants et flags locaux.
- Générer l'aide et l'autocomplétion.
Installer Cobra
Section intitulée « Installer Cobra »Cobra est une dépendance de votre module, épinglée dans go.mod :
go mod init example/scango get github.com/spf13/cobra@v1.10.2La commande racine
Section intitulée « La commande racine »Tout part d'une commande racine de type *cobra.Command. Son champ Use donne le nom de l'outil, Short le résumé affiché dans l'aide. La méthode Execute() lance le parsing.
package main
import ( "fmt" "os"
"github.com/spf13/cobra")
var verbose bool
func main() { root := &cobra.Command{ Use: "scan", Short: "scan inspecte des cibles locales et distantes", } // Flag persistant : hérité par toutes les sous-commandes. root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "sortie détaillée")
if err := root.Execute(); err != nil { os.Exit(1) }}Ajouter des sous-commandes
Section intitulée « Ajouter des sous-commandes »Une sous-commande est elle-même un *cobra.Command, rattachée à la racine par AddCommand. Sa logique vit dans RunE, la variante de Run qui renvoie une erreur : Cobra l'affiche et positionne le code de sortie. Ajoutons deux sous-commandes, files et ports, chacune avec ses flags locaux.
var path stringfilesCmd := &cobra.Command{ Use: "files", Short: "Compte les entrées d'un répertoire", RunE: func(cmd *cobra.Command, args []string) error { entries, err := os.ReadDir(path) if err != nil { return err } if verbose { fmt.Fprintf(os.Stderr, "lecture de %s\n", path) } fmt.Printf("%d entrées dans %s\n", len(entries), path) return nil },}filesCmd.Flags().StringVar(&path, "path", ".", "répertoire à inspecter")
var host stringvar top intportsCmd := &cobra.Command{ Use: "ports", Short: "Liste les ports courants à tester sur un hôte", RunE: func(cmd *cobra.Command, args []string) error { common := []int{22, 80, 443, 5432, 6379, 8080, 9090} if top > len(common) { top = len(common) } fmt.Printf("%d ports courants pour %s : %v\n", top, host, common[:top]) return nil },}portsCmd.Flags().StringVar(&host, "host", "localhost", "hôte cible")portsCmd.Flags().IntVar(&top, "top", 3, "nombre de ports à afficher")
root.AddCommand(filesCmd, portsCmd)Flags persistants et flags locaux
Section intitulée « Flags persistants et flags locaux »La distinction est importante. Un flag persistant, déclaré sur PersistentFlags(), est disponible sur la commande et toutes ses sous-commandes : --verbose fonctionne partout. Un flag local, déclaré sur Flags(), n'existe que pour sa commande : --path n'a de sens que pour files.
| Type | Déclaration | Portée |
|---|---|---|
| Persistant | PersistentFlags() | La commande et toute sa descendance |
| Local | Flags() | La commande seule |
Exécuter l'outil
Section intitulée « Exécuter l'outil »Compilez, puis exécutez les sous-commandes. L'aide est générée automatiquement à partir des champs Short et des flags déclarés :
go run . --helpscan inspecte des cibles locales et distantes
Usage: scan [command]
Available Commands: completion Generate the autocompletion script for the specified shell files Compte les entrées d'un répertoire help Help about any command ports Liste les ports courants à tester sur un hôteChaque sous-commande fait son travail, avec ses flags :
go run . files --path /etc89 entrées dans /etcgo run . ports --host db.internal --top 44 ports courants pour db.internal : [22 80 443 5432]Cobra a géré le routage vers la bonne sous-commande, la conversion du flag --top en entier et la valeur par défaut de --host.
L'autocomplétion générée
Section intitulée « L'autocomplétion générée »Cobra ajoute d'office une commande completion qui produit le script d'autocomplétion pour bash, zsh, fish ou PowerShell. L'utilisateur l'installe une fois, et la tabulation complète alors commandes et flags :
# Pour zsh, le temps de la sessionsource <(scan completion zsh)Bonnes pratiques
Section intitulée « Bonnes pratiques »- Utilisez
RunE, pasRun: renvoyer l'erreur laisse Cobra gérer l'affichage et le code de sortie. - Un flag persistant pour le transverse (
--verbose,--config), un flag local pour le spécifique. - Gardez
mainmince : la logique métier va dans des paquets, la commande ne fait que l'appeler. - Épinglez la version de Cobra dans
go.modpour un build reproductible. - Laissez Cobra générer l'aide : ne réécrivez pas à la main ce que les champs
Short/Longproduisent.
À retenir
Section intitulée « À retenir »- Cobra structure une CLI en un arbre de
*cobra.Command, commekubectloudocker. AddCommandrattache les sous-commandes ;RunEporte leur logique et remonte les erreurs.- Flags persistants (hérités) et flags locaux (propres) couvrent les deux besoins.
- L'aide et l'autocomplétion sont générées, pas écrites à la main.
- Couplé à Viper, Cobra unifie flags, environnement et fichier de config.