web-dev-qa-db-fra.com

Quelles sont les bonnes habitudes pour concevoir des arguments de ligne de commande?

Lors du développement de l'application, j'ai commencé à me demander - Comment concevoir des arguments de ligne de commande?

De nombreux programmes utilisent une formule comme celle-ci -argument value ou /argument value. La solution qui m'est venue à l'esprit était argument:value. Je pensais que c'était bien car sans espaces blancs, il n'y a aucun moyen de gâcher les valeurs et les arguments. Il est également facile de diviser une chaîne en deux sur la première à partir de la gauche : personnage.

Mes questions sont:

  1. Est populaire -argument value formule meilleure que argument:value (plus lisible, plus facile à écrire, sans bug, plus facile à comprendre par les développeurs experts)?
  2. Existe-t-il des règles connues que je dois suivre lors de la conception des arguments de ligne de commande (à part si cela fonctionne, c'est OK)?

Interrogé pour plus de détails, je vous le fournirai. Cependant, je pense qu'ils ne devraient pas affecter les réponses. La question porte sur de bonnes habitudes en général. Je pense qu'ils sont tous les mêmes pour toutes sortes d'applications.

Nous travaillons sur une application qui sera utilisée dans les lieux publics (totems tactiles, tables). Les applications sont écrites à l'aide de Qt Quick 5 (C++, QML, JS). Windows 8.1/10 sera installé sur les appareils. Nous fournirons une interface frontale pour gérer les appareils. Cependant, certains administrateurs avancés peuvent souhaiter configurer l'application par eux-mêmes. Ce n'est pas très important du côté de l'entreprise, mais comme je suis d'accord avec ce que Kilian Foth a dit, je ne veux pas que mon application soit une douleur pour un utilisateur. Ne trouvant pas sur Internet ce que je veux, j'ai demandé ici.


Pour les utilisateurs plus avancés de Stack Exchange: je voulais que cette question soit générale. Peut-être qu'il se qualifie pour le wiki de la communauté (je ne sais pas si la question existante peut être convertie avec des réponses). Comme je veux que cette question soit indépendante du système d'exploitation et du langage de programmation, les réponses qui apparaissent ici peuvent être une leçon précieuse pour d'autres développeurs.

195
Filip Hazubski

Sur les systèmes POSIX (par exemple Linux, MacOSX), au moins pour les programmes éventuellement démarrés dans un terminal Shell (par exemple la plupart d'entre eux), je recommanderais d'utiliser les conventions de codage GN (qui répertorie également les noms d'arguments courants) et examinez les directives des utilitaires POSIX , même pour les logiciels propriétaires:

  • toujours gérer --version et --help (même /bin/true les accepte !!). Je maudis les auteurs de logiciels qui ne comprennent pas --help, Je les déteste (car prog --help Est la première commande que j'essaie sur un nouveau programme) ! Souvent, --help Peut être abrégé en -h

  • Demandez au message --help De répertorier toutes les options (sauf si vous en avez trop ... dans ce cas, répertoriez les plus courantes et explicitement faites référence à certains man page ou une URL) et les valeurs par défaut des options, et peut-être des variables d'environnement importantes (et spécifiques au programme). Afficher ces listes d'options en cas d'erreur d'argument d'option.

  • accepter -a argument court (lettre unique) et avoir un équivalent --long-argument, donc -a2--long-argument=2, --long-argument 2; bien sûr, vous pourriez avoir (pour les options rarement utilisées) un nom --only-long-argument; pour les arguments modaux sans options supplémentaires -cf est généralement traité comme -c -f, etc. donc votre proposition -argument:value est bizarre, et je ne recommande pas de le faire.

  • utilisez GLIBC getopt_long ou mieux (par exemple argp_parse , dans OCaml c'est Arg module , ...)

  • utilise souvent - pour l'entrée ou la sortie standard (si vous ne pouvez pas le faire, gérez /dev/stdin & /dev/stdout même sur les quelques systèmes d'exploitation qui n'en ont pas)

  • imite le comportement de programmes similaires en réutilisant la plupart de leurs conventions d'options; en particulier -n pour la marche à sec (à la make), -h pour l'aide, -v pour la verbosité, etc ...

  • utilisez -- comme séparateur entre les options et le fichier ou d'autres arguments

  • si votre programme utilise isatty pour tester que stdin est un terminal (et se comporte "interactivement" dans ce cas), fournissez une option pour forcer le mode non interactif, de même si votre programme a une interface graphique (et teste getenv("DISPLAY") sur le bureau X11) mais peut également être utilisé en batch ou en ligne de commande.

  • Certains programmes (par exemple gcc) acceptent les listes d'arguments indirects, donc @somefile.txt Signifie des arguments de programme lus dans somefile.txt; cela peut être utile lorsque votre programme peut accepter un très grand nombre d'arguments (plus que le ARG_MAX de votre noyau)

BTW, vous pouvez même ajouter des fonctionnalités de saisie semi-automatique pour votre programme et les shells habituels (comme bash ou zsh)

Certaines anciennes commandes Unix (par exemple dd, ou même sed) ont des arguments de commande étranges pour la compatibilité historique. Je recommanderais de ne pas suivre leurs mauvaises habitudes (sauf si vous en faites une meilleure variante).

Si votre logiciel est une série de programmes en ligne de commande, inspirez-vous de git (que vous utilisez sûrement comme outil de développement), qui accepte git help Et git --help et avoir plusieurs gitsubcommand et gitsubcommand--help

Dans de rares cas, vous pouvez également utiliser argv[0] (En utilisant des liens symboliques sur votre programme), par exemple bash appelé comme rbash a un comportement différent ( restreint Shell). Mais je ne recommande généralement pas de faire cela; cela pourrait avoir du sens si votre programme pouvait être utilisé comme interpréteur de script en utilisant Shebang c'est-à-dire #! sur la première ligne interprété par execve (2) . Si vous faites de telles astuces, assurez-vous de les documenter, y compris dans les messages --help.

Souvenez-vous que sur POSIX le Shell est globbing arguments ( avant d'exécuter votre programme!), Donc évitez nécessitant des caractères (comme * ou $ ou ~) dans des options qui devraient être échappées par Shell.

Dans certains cas, vous pouvez intégrer un interpréteur comme GNU guile ou Lua dans votre logiciel (évitez d'inventer votre propre Turing-complete langage de script si vous ne sont pas experts en langages de programmation). Cela a des conséquences profondes sur la conception de votre logiciel (donc pensez-y tôt!). Vous devriez alors pouvoir facilement transmettre un script ou une expression à cet interprète. Si vous adoptez cette approche intéressante, concevez votre logiciel et ses primitives interprétées avec soin; vous pourriez avoir des utilisateurs étranges qui codent de gros scripts pour votre truc.

Dans d'autres cas, vous voudrez peut-être laisser vos utilisateurs avancés charger leur plugin dans votre logiciel (en utilisant chargement dynamique techniques à la dlopen & dlsym). Encore une fois, il s'agit d'une décision de conception très importante (définissez et documentez donc l'interface du plug-in avec soin), et vous devrez définir une convention pour transmettre les options du programme à ces plug-ins.

Si votre logiciel est une chose complexe, faites-le accepter fichiers de configuration (en plus ou en remplacement des arguments du programme) et avez probablement un moyen de tester (ou simplement analyser) ces fichiers de configuration sans exécuter tout le code . Par exemple, un agent de transfert de courrier (comme Exim ou Postfix) est assez complexe, et il est utile de pouvoir l'exécuter "à moitié sec" (par exemple en observant comment il gère une adresse e-mail donnée sans réellement envoyer d'e-mail).


Notez que le /option Est une chose Windows ou VMS. Ce serait insensé sur les systèmes POSIX (parce que la hiérarchie des fichiers utilise / Comme séparateur de répertoires, et parce que le shell effectue la globalisation). Toute ma réponse est principalement pour Linux (et POSIX).


P.S. Si possible, faites de votre programme un logiciel gratuit , vous obtiendrez des améliorations de la part de certains utilisateurs et développeurs (et l'ajout d'une nouvelle option de programme est souvent l'une des choses les plus faciles à ajouter à un logiciel gratuit existant). De plus, votre question dépend beaucoup du public visé : un jeu pour adolescents ou un navigateur pour grand-mère n'a probablement pas besoin du même type et de la même quantité d'options qu'un compilateur, ou un inspecteur de réseau pour les administrateurs système de centre de données, ou un logiciel CAD pour les architectes à microprocesseurs ou les concepteurs de ponts. Un ingénieur familier avec la programmation & les scripts aiment probablement beaucoup plus avoir beaucoup d'options réglables que votre grand-mère, et voudront probablement pouvoir exécuter votre application sans X11 (peut-être dans un travail crontab).

239

Le fait qu'une convention de format de données soit populaire est son avantage.

Vous pouvez facilement voir que l'utilisation de = ou: ou même '' comme séparateur sont des différences triviales qui pourraient être converties les unes par les autres par ordinateur sans effort. Ce qui serait un gros effort est pour un humain de se souvenir "Maintenant, voyez-vous, ce programme rarement utilisé délimitait-il les choses avec : ou avec =? Hmmm ... "

En d'autres termes, pour l'amour de Dieu, ne déviez pas des conventions extrêmement bien ancrées sans raison impérieuse. Les gens se souviendront de votre programme comme "celui avec la syntaxe cmdline étrange et ennuyeuse" au lieu de "celui qui a sauvé mon essai collégial".

69
Kilian Foth

En termes simples

Quand à Rome, faites comme les Romains.

  • Si votre application CLI est destinée à Linux/Unix, utilisez le -p value ou --parameter value convention. Linux dispose d'outils pour analyser ces paramètres et indicateurs de manière simple.

Je fais habituellement quelque chose comme ça:

while [[ $# > 0 ]]
do
key="$1"
case $key in
    --dummy)
    #this is a flag do something here
    ;;
    --audit_sessiones)
    #this is a flag do something here
    ;;
    --destination_path)
    # this is a key-value parameter
    # the value is always in $2 , 
    # you must shift to skip over for the next iteration
    path=$2
    shift
    ;;
    *)
    # unknown option
    ;;
esac
shift
done
  • Si votre application CLI est destinée à Windows, utilisez /flag et /flag:value conventions.

  • Certaines applications comme Oracle n'utilisent cependant ni l'un ni l'autre. Les utilitaires Oracle utilisent PARAMETER=VALUE.

  • Une chose que j'aime faire est, en plus d'accepter des paramètres dans la ligne de commande, offrant la possibilité d'utiliser un parfile, qui est un fichier de paire clé-valeur pour éviter les longues chaînes de paramètres. Pour cela, vous devez fournir un --parfile mifile.par paramètre. Évidemment, si --parfile est utilisé, tous les autres paramètres sont supprimés au profit de ce qui se trouve à l'intérieur du parfile.

  • Une suggestion supplémentaire autorise l'utilisation de certaines variables d'environnement personnalisées, par exemple, en définissant la variable d'environnement MYAPP_WRKSPACE=/tmp rendrait inutile de toujours définir --wrkspace /tmp.

  • Sous Linux, n'oubliez pas d'ajouter auto-complétion des paramètres, ce qui signifie que les utilisateurs peuvent taper un demi-interrupteur, appuyer sur TAB puis Shell le complétera pour eux.
29
Tulains Córdova

Une chose qui n'est pas encore apparue:

Essayez de concevoir votre logiciel à partir des arguments de la ligne de commande vers le haut. Sens:

Avant de concevoir la fonctionnalité, concevez l'interface utilisateur.

Cela vous permettra d'explorer les cas Edge et les cas courants très tôt. Bien sûr, vous abstiendrez toujours l'extérieur et l'intérieur, mais cela donnera de bien meilleurs résultats que d'écrire tout le code et de claquer une CLI dessus.

De plus, consultez docopt ( http://docopt.org/ ).

docopt est d'une grande aide pour cela dans de nombreuses langues, en particulier pour python où vous avez des analyseurs d'arguments très limités et défavorables pour l'utilisateur comme argparse toujours considérés comme "OK". Au lieu d'avoir des analyseurs et des sous-analyseurs et les dict conditionnels, il vous suffit de définir l'aide de la syntaxe et il fait le reste.

19
Florian Heigl

Certains commentaires précieux ont déjà été fournis (@Florian, Basile), mais permettez-moi d'ajouter ... OP dit,

Nous fournirons une interface frontale pour gérer les appareils. Cependant, certains administrateurs avancés peuvent souhaiter configurer l'application eux-mêmes

Mais remarque également:

Je ne voulais pas que cette question soit spécifique à la plate-forme ou à la langue

Vous devez considérer votre public cible - les administrateurs avancés . Sur quelle plateforme fonctionnent-ils normalement - Win/Unix/Mac? Et sur quelle plateforme votre application fonctionne-t-elle? Suivez les conventions CLI déjà établies pour cette plate-forme. Vos administrateurs "avancés" veulent/ont-ils besoin d'un outil basé sur une interface graphique?

Vous souhaitez que l'interface soit cohérente en interne et avec d'autres outils d'administration. Je ne veux pas m'arrêter et je pense que c'est cmd -p <arg> ou cmd -p:<arg> ou cmd /p <arg>. Ai-je besoin de citations car il y a un espace? Puis-je cmd -p <val1> <val2> ou cmd -p <val1> -p <val2> pour plusieurs cibles? Sont-ils spécifiques à la commande? Surchargeable? Est-ce que cmd -p2 <arg> -p1 <arg> travailler aussi? Est-ce que ls -l -r -t dir1 dir2 == ls -trl dir1 dir2?

Pour mes outils d'administration Unix, j'ai toujours gardé à l'esprit les conseils fournis par Heiner's Shelldorado ainsi que les autres références mentionnées.

Aussi important que la conception de l'interface CLI est de vous assurer que votre application est conçue pour fonctionner avec des arguments de ligne de commande identiques à ceux de l'interface graphique - c'est-à-dire: pas de logique métier dans l'interface graphique ou utiliser la commande commune appelée à partir de l'interface graphique et de la CLI.

La plupart des outils d'administration basés sur UNIX sont en fait conçus d'abord comme des outils de ligne de commande et l'interface graphique fournie facilite simplement le "remplissage" des options de la ligne de commande. Cette approche permet l'automatisation, l'utilisation des fichiers de réponses, etc. et la gestion du transfert (moins de travail pour moi!)

Comme le jeu d'outils classique utilisé avec cette approche est Tcl/Tk . Ne pas suggérer de changer d'outils; il suffit de considérer l'approche de conception, de l'écriture d'une application d'administration basée sur une interface graphique à l'application en tant qu'outil de ligne de commande; puis superposez l'interface graphique pour plus de commodité. À un certain point, vous découvrirez probablement que l'interface graphique est une douleur (et sujette aux erreurs) si vous devez effectuer plusieurs configurations et ressaisir généralement les mêmes options encore et encore et que vous rechercherez une approche automatisée.

N'oubliez pas que votre administrateur doit probablement saisir les valeurs correctes dans les bonnes cases de toute façon, alors combien d'efforts soulagez-vous de toute façon avec l'interface graphique?

3
Ian W