🔝 Retour au Sommaire
L'aide en ligne de commande (--help) est souvent le premier contact qu'un utilisateur a avec votre outil. Une aide bien structurée, complète et lisible fait la différence entre un outil adopté par l'équipe et un outil que personne n'utilise parce que "personne ne sait comment ça marche". CLI11 génère cette aide automatiquement à partir des déclarations d'options, mais elle offre aussi un contrôle fin sur la présentation pour les projets qui ont besoin d'une finition professionnelle.
Sans aucune configuration supplémentaire, CLI11 produit une aide structurée à partir des informations déjà déclarées. Reprenons un exemple concret :
CLI::App app{"logwatch — Surveillance de fichiers de log en temps réel"};
app.set_version_flag("--version,-V", "logwatch 2.1.0");
std::string filepath;
app.add_option("file", filepath, "Fichier de log à surveiller")
->required()
->check(CLI::ExistingFile);
int lines = 20;
app.add_option("--lines,-n", lines, "Nombre de lignes initiales à afficher")
->check(CLI::Range(1, 10000))
->capture_default_str();
std::string pattern;
app.add_option("--grep,-g", pattern, "Filtrer par expression régulière");
std::string level = "info";
app.add_option("--level,-l", level, "Niveau de log minimum")
->check(CLI::IsMember({"trace", "debug", "info", "warn", "error", "fatal"}))
->capture_default_str();
bool follow = false;
app.add_flag("--follow,-f", follow, "Suivre le fichier en temps réel (tail -f)");
bool color = true;
app.add_flag("--color,!--no-color", color, "Activer/désactiver la colorisation");
std::string format = "text";
app.add_option("--format", format, "Format de sortie")
->check(CLI::IsMember({"text", "json", "compact"}))
->capture_default_str();L'invocation logwatch --help produit :
logwatch — Surveillance de fichiers de log en temps réel
Usage: logwatch [OPTIONS] file
Positionals:
file TEXT:FILE REQUIRED Fichier de log à surveiller
Options:
-h,--help Print this help message and exit
--version,-V Display program version information and exit
--lines,-n INT:INT in [1 - 10000] [20]
Nombre de lignes initiales à afficher
--grep,-g TEXT Filtrer par expression régulière
--level,-l TEXT:{trace,debug,info,warn,error,fatal} [info]
Niveau de log minimum
--follow,-f Suivre le fichier en temps réel (tail -f)
--color,--no-color{false} Activer/désactiver la colorisation
--format TEXT:{text,json,compact} [text]
Format de sortie
Observons ce que CLI11 a généré automatiquement :
- La description de l'application en titre.
- La ligne d'usage synthétique (
Usage: logwatch [OPTIONS] file). - La section Positionals avec le type attendu et le marqueur
REQUIRED. - La section Options avec, pour chaque option : les noms long et court, le type, les contraintes (intervalle, membres), la valeur par défaut entre crochets, et la description.
- Les flags
--helpet--versionajoutés automatiquement.
Tout cela provient des informations déjà déclarées dans le code — descriptions, validateurs, capture_default_str(). L'aide n'est jamais écrite manuellement, elle reste synchronisée avec le code par construction.
À mesure que le nombre d'options grandit, une liste plate devient difficile à parcourir. CLI11 permet de regrouper les options par thème via la méthode ->group() :
// Groupe "Filtrage"
app.add_option("--grep,-g", pattern, "Filtrer par expression régulière")
->group("Filtrage");
app.add_option("--level,-l", level, "Niveau de log minimum")
->check(CLI::IsMember({"trace", "debug", "info", "warn", "error", "fatal"}))
->capture_default_str()
->group("Filtrage");
// Groupe "Affichage"
app.add_option("--lines,-n", lines, "Nombre de lignes initiales")
->check(CLI::Range(1, 10000))
->capture_default_str()
->group("Affichage");
app.add_flag("--follow,-f", follow, "Suivre le fichier en temps réel")
->group("Affichage");
app.add_flag("--color,!--no-color", color, "Activer/désactiver la colorisation")
->group("Affichage");
app.add_option("--format", format, "Format de sortie")
->check(CLI::IsMember({"text", "json", "compact"}))
->capture_default_str()
->group("Affichage");L'aide résultante est organisée par sections :
logwatch — Surveillance de fichiers de log en temps réel
Usage: logwatch [OPTIONS] file
Positionals:
file TEXT:FILE REQUIRED Fichier de log à surveiller
Filtrage:
--grep,-g TEXT Filtrer par expression régulière
--level,-l TEXT:{trace,debug,info,warn,error,fatal} [info]
Niveau de log minimum
Affichage:
--lines,-n INT:INT in [1 - 10000] [20]
Nombre de lignes initiales
--follow,-f Suivre le fichier en temps réel
--color,--no-color{false} Activer/désactiver la colorisation
--format TEXT:{text,json,compact} [text]
Format de sortie
Options:
-h,--help Print this help message and exit
--version,-V Display program version information and exit
Les groupes apparaissent dans l'ordre de leur première déclaration dans le code. Les options sans groupe explicite tombent dans le groupe par défaut ("Options"). Ce regroupement est une amélioration d'ergonomie significative dès que l'outil dépasse cinq ou six options.
Chaque sous-commande a sa propre aide, accessible via <tool> <subcommand> --help. CLI11 génère aussi un résumé des sous-commandes dans l'aide principale.
CLI::App app{"dbcli — Outil de gestion de base de données"};
app.require_subcommand(1);
auto* cmd_migrate = app.add_subcommand("migrate", "Gérer les migrations de schéma");
auto* cmd_seed = app.add_subcommand("seed", "Peupler avec des données de test");
auto* cmd_backup = app.add_subcommand("backup", "Sauvegarder la base de données");
auto* cmd_restore = app.add_subcommand("restore", "Restaurer depuis une sauvegarde"); L'aide principale affiche la liste des sous-commandes :
$ dbcli --help
dbcli — Outil de gestion de base de données
Usage: dbcli [OPTIONS] SUBCOMMAND
Options:
-h,--help Print this help message and exit
Subcommands:
migrate Gérer les migrations de schéma
seed Peupler avec des données de test
backup Sauvegarder la base de données
restore Restaurer depuis une sauvegarde
Et chaque sous-commande affiche ses propres options :
$ dbcli backup --help
Sauvegarder la base de données
Usage: dbcli backup [OPTIONS]
Options:
-h,--help Print this help message and exit
--output,-o TEXT REQUIRED Fichier de destination
--compress,-c Compresser la sauvegarde (gzip)
--tables TEXT ... Tables à sauvegarder (toutes par défaut)
Pour les outils avec de nombreuses sous-commandes, on peut les regrouper thématiquement :
auto* cmd_migrate = app.add_subcommand("migrate", "Gérer les migrations");
cmd_migrate->group("Schéma");
auto* cmd_seed = app.add_subcommand("seed", "Peupler avec des données de test");
cmd_seed->group("Schéma");
auto* cmd_backup = app.add_subcommand("backup", "Sauvegarder la base");
cmd_backup->group("Opérations");
auto* cmd_restore = app.add_subcommand("restore", "Restaurer une sauvegarde");
cmd_restore->group("Opérations"); $ dbcli --help
dbcli — Outil de gestion de base de données
Usage: dbcli [OPTIONS] SUBCOMMAND
Options:
-h,--help Print this help message and exit
Schéma:
migrate Gérer les migrations
seed Peupler avec des données de test
Opérations:
backup Sauvegarder la base
restore Restaurer une sauvegarde
CLI11 utilise un objet Formatter pour produire le texte d'aide. Le formatter par défaut couvre la plupart des besoins, mais on peut le configurer ou le remplacer entièrement.
Le formatter par défaut aligne les descriptions dans deux colonnes. On peut ajuster la largeur :
auto fmt = std::make_shared<CLI::Formatter>();
fmt->column_width(40); // Largeur de la colonne des noms d'options
app.formatter(fmt); Une largeur de colonne plus grande est utile quand les noms d'options sont longs (options avec validateurs complexes). Une largeur plus petite convient aux outils avec des noms courts et concis.
CLI11 permet d'ajouter un texte libre en bas de l'aide, après la liste des options. C'est l'endroit idéal pour des exemples d'utilisation, des notes ou des liens :
app.footer(
"Exemples:\n"
" logwatch /var/log/syslog --follow --level warn\n"
" logwatch app.log -n 100 --grep 'ERROR|FATAL' --format json\n"
" logwatch access.log -f --no-color | jq '.'\n"
"\n"
"Variables d'environnement:\n"
" LOGWATCH_CONFIG Chemin du fichier de configuration\n"
" LOGWATCH_LEVEL Niveau de log par défaut\n"
"\n"
"Documentation : https://example.com/logwatch"
);Résultat à la fin de --help :
...
Exemples:
logwatch /var/log/syslog --follow --level warn
logwatch app.log -n 100 --grep 'ERROR|FATAL' --format json
logwatch access.log -f --no-color | jq '.'
Variables d'environnement:
LOGWATCH_CONFIG Chemin du fichier de configuration
LOGWATCH_LEVEL Niveau de log par défaut
Documentation : https://example.com/logwatch
Le footer est un élément crucial d'ergonomie. Les utilisateurs de CLI lisent rarement la documentation complète — mais ils lisent le --help. Inclure deux ou trois exemples concrets couvrant les cas d'usage les plus courants accélère considérablement l'adoption.
Pour une description plus longue que la ligne de titre, on peut modifier la description de l'application avec un texte multi-lignes :
app.description(
"logwatch — Surveillance de fichiers de log en temps réel\n"
"\n"
"Surveille un fichier de log et affiche les nouvelles entrées\n"
"avec filtrage par niveau, expression régulière et colorisation\n"
"automatique. Supporte la sortie JSON pour l'intégration dans\n"
"des pipelines de monitoring."
);Certaines options ne sont pas destinées à l'utilisateur final : options de débogage interne, flags expérimentaux, ou options de compatibilité ascendante qu'on souhaite maintenir sans les promouvoir. CLI11 permet de les masquer :
// Option masquée — fonctionnelle mais absente du --help
bool debug_internals = false;
app.add_flag("--debug-internals", debug_internals,
"Activer le débogage interne")
->group(""); // Groupe vide = masqué dans l'aideL'option reste pleinement fonctionnelle sur la ligne de commande, elle n'apparaît simplement pas dans le --help. C'est le pattern standard pour les options de débogage ou les fonctionnalités en cours de développement.
Pour regrouper explicitement les options avancées dans un groupe visible mais distinct :
// Options avancées, visibles mais clairement séparées
app.add_option("--buffer-size", buffer_sz, "Taille du buffer interne (octets)")
->group("Options avancées")
->capture_default_str();
app.add_option("--poll-interval", poll_ms, "Intervalle de polling (ms)")
->group("Options avancées")
->capture_default_str();Au-delà de --help, il est parfois utile d'accéder au texte d'aide de manière programmatique — pour l'écrire dans un fichier, l'intégrer dans une documentation, ou l'afficher conditionnellement.
std::string help_text = app.help();app.help() retourne exactement le texte qui serait affiché par --help. On peut l'utiliser dans un callback pour afficher l'aide contextuelle :
app.callback([&]() {
// Si aucune sous-commande n'a été invoquée, afficher l'aide
if (app.get_subcommands().empty()) {
std::println("{}", app.help());
}
});auto* cmd_build = app.add_subcommand("build", "Compiler le projet");
// ... options ...
// Plus tard, récupérer l'aide de "build" :
std::string build_help = cmd_build->help();Pour des cas avancés (génération de documentation, complétion shell, introspection), CLI11 permet de parcourir les options enregistrées :
for (const auto* opt : app.get_options()) {
std::println("Option : {} (requis: {}, type: {})",
opt->get_name(),
opt->get_required(),
opt->get_type_name());
}Ce mécanisme d'introspection est la base sur laquelle sont construits les générateurs de complétion.
L'autocomplétion dans le shell est un confort majeur pour les utilisateurs réguliers d'un outil CLI. CLI11 ne génère pas directement les scripts de complétion, mais son API d'introspection rend l'exercice réalisable.
Le pattern courant consiste à générer un script Bash à partir de la liste des options et sous-commandes :
auto* cmd_completion = app.add_subcommand("completion",
"Générer un script de complétion shell");
std::string shell = "bash";
cmd_completion->add_option("shell", shell, "Shell cible")
->check(CLI::IsMember({"bash", "zsh", "fish"}))
->capture_default_str();
cmd_completion->callback([&]() {
if (shell == "bash") {
std::println("# Complétion Bash pour {}", app.get_name());
std::println("# Ajouter à ~/.bashrc :");
std::println("# eval \"$({} completion bash)\"", app.get_name());
std::println("");
std::println("_{}_completions() {{", app.get_name());
std::println(" local cur=\"${{COMP_WORDS[COMP_CWORD]}}\"");
// Sous-commandes
std::print(" local commands=\"");
for (const auto* sub : app.get_subcommands({})) {
std::print("{} ", sub->get_name());
}
std::println("\"");
// Options globales
std::print(" local global_opts=\"");
for (const auto* opt : app.get_options()) {
if (opt->get_name() != "--help" && opt->get_name() != "--version") {
std::print("{} ", opt->get_name());
}
}
std::println("\"");
std::println(" COMPREPLY=( $(compgen -W "
"\"$commands $global_opts\" -- \"$cur\") )");
std::println("}}");
std::println("complete -F _{0}_completions {0}", app.get_name());
}
// ... zsh, fish ...
});# Installer la complétion
$ eval "$(logwatch completion bash)"
# Utilisation : Tab-complétion active
$ logwatch --<TAB>
--color --follow --format --grep --help
--level --lines --no-color --versionCe script basique peut être enrichi pour gérer la complétion contextuelle par sous-commande, la complétion de valeurs pour IsMember, et la complétion de chemins de fichiers pour les options marquées ExistingFile. L'investissement en vaut la peine pour un outil distribué largement.
Les outils CLI les mieux conçus partagent des conventions dans leur aide. Les appliquer rend votre outil immédiatement familier :
La ligne Usage: suit un format standardisé :
Usage: <tool> [OPTIONS] <required_args> [optional_args...]
[OPTIONS]entre crochets : optionnel.<arg>entre chevrons : obligatoire.[arg]entre crochets : optionnel.arg...avec points de suspension : répétable.SUBCOMMANDen majuscules : un choix parmi les sous-commandes.
CLI11 génère cette ligne automatiquement et la met à jour quand vous ajoutez ou retirez des options.
L'ordre conventionnel, suivi par la plupart des outils Unix, est :
1. Description (une phrase ou un paragraphe)
2. Usage (ligne synthétique)
3. Positionnels (arguments obligatoires)
4. Groupes d'options (par thème)
5. Options générales (--help, --version)
6. Sous-commandes (si applicable)
7. Footer (exemples, notes, liens)
CLI11 suit cet ordre par défaut. Les groupes d'options apparaissent dans l'ordre de première déclaration, ce qui vous donne un contrôle implicite sur le séquencement.
Quelques conventions à suivre pour des descriptions cohérentes :
// ✓ Commencer par un verbe ou un nom, sans majuscule initiale,
// sans point final, sur une seule ligne
"Filtrer par expression régulière"
"Nombre de lignes initiales à afficher"
"Suivre le fichier en temps réel (tail -f)"
// ✓ Mentionner l'unité quand c'est pertinent
"Timeout en millisecondes"
"Taille maximale en Mo"
// ✓ Mentionner le comportement par défaut quand il n'est pas évident
"Répertoire de sortie (défaut: répertoire courant)"
// ✗ Trop long — l'aide doit être scannable, pas lue en détail
"Spécifie le nombre maximal de lignes qui seront affichées au
démarrage de l'outil, avant de passer en mode surveillance"Pour les projets qui ont besoin d'un contrôle total sur le rendu de l'aide, CLI11 permet de remplacer le formatter par une implémentation personnalisée. On hérite de CLI::Formatter et on surcharge les méthodes souhaitées :
class CompactFormatter : public CLI::Formatter {
public:
// Personnaliser le label de chaque option
std::string make_option_opts(const CLI::Option* opt) const override {
std::string out;
if (opt->get_type_name() != "BOOLEAN") {
out += " " + opt->get_type_name();
}
if (opt->get_required()) {
out += " (requis)";
}
if (!opt->get_default_str().empty()) {
out += " [=" + opt->get_default_str() + "]";
}
return out;
}
// Personnaliser le séparateur entre les groupes
std::string make_group(std::string group,
bool is_positional,
std::vector<const CLI::Option*> opts) const override {
std::string out = "\n── " + group + " ";
out += std::string(50 - group.size(), '─') + "\n";
for (const auto* opt : opts) {
out += make_option(opt, is_positional);
}
return out;
}
};
// Application du formatter
app.formatter(std::make_shared<CompactFormatter>());Les méthodes surchargeables les plus utiles :
| Méthode | Contrôle |
|---|---|
make_help |
Le texte d'aide complet |
make_description |
Le bloc de description |
make_usage |
La ligne Usage: |
make_group |
Un groupe d'options (titre + liste) |
make_option |
Une ligne d'option individuelle |
make_option_opts |
Les métadonnées d'une option (type, défaut) |
make_option_desc |
La description d'une option |
make_subcommands |
La section des sous-commandes |
make_footer |
Le footer |
En pratique, le formatter par défaut convient à la grande majorité des projets. La personnalisation est utile pour des outils destinés à un large public où l'image de marque et la cohérence visuelle avec d'autres outils de l'écosystème importent — par exemple, reproduire le style d'aide de kubectl ou de cargo.
Certains outils exposent l'aide non seulement via --help, mais aussi via une sous-commande help qui accepte un argument :
$ git help commit # Équivalent de git commit --help
$ kubectl help get pods # Aide pour "kubectl get pods"CLI11 ne fournit pas ce pattern nativement, mais il s'implémente en quelques lignes :
auto* cmd_help = app.add_subcommand("help", "Afficher l'aide d'une commande");
std::string help_topic;
cmd_help->add_option("command", help_topic, "Sous-commande");
cmd_help->callback([&]() {
if (help_topic.empty()) {
std::println("{}", app.help());
return;
}
auto* sub = app.get_subcommand(help_topic);
if (sub) {
std::println("{}", sub->help());
} else {
std::println(stderr, "Commande inconnue : {}", help_topic);
std::println("{}", app.help());
}
});$ mon-outil help
# Affiche l'aide générale
$ mon-outil help backup
# Affiche l'aide de la sous-commande "backup"
$ mon-outil help inexistant
Commande inconnue : inexistant
# Puis affiche l'aide générale| Technique | Méthode CLI11 | Effet |
|---|---|---|
| Description de l'app | app.description(text) ou constructeur |
Titre/description en haut de l'aide |
| Version | app.set_version_flag("--version,-V", str) |
Flag --version automatique |
| Grouper des options | ->group("Nom") |
Sections thématiques dans l'aide |
| Masquer une option | ->group("") |
Option fonctionnelle mais invisible |
| Valeur par défaut | ->capture_default_str() |
Affiche [valeur] dans l'aide |
| Exemples et notes | app.footer(text) |
Texte libre en bas de l'aide |
| Aide programmatique | app.help() |
Retourne l'aide sous forme de chaîne |
| Introspection | app.get_options() |
Liste des options déclarées |
| Largeur des colonnes | formatter->column_width(n) |
Ajuste l'alignement |
| Formatter personnalisé | app.formatter(shared_ptr) |
Contrôle total du rendu |
Sous-commande help |
Implémentation manuelle | Pattern tool help <cmd> |
Cette section conclut la couverture de CLI11. Vous disposez maintenant de tous les éléments pour construire des interfaces CLI de qualité professionnelle : parsing typé (36.1.1), options et sous-commandes (36.1.2), validation et callbacks (36.1.3), et aide soignée (36.1.4). La section suivante (36.2) présente argparse, une alternative plus légère pour les outils à l'interface plus simple.