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

190

Tout en développant l'application, j'ai commencé à me demander: comment concevoir des arguments en ligne de commande?

Beaucoup de programmes utilisent une formule comme celle-ci -argument valueou /argument value. La solution qui m'est venue à l’esprit était argument:value. Je pensais que c'était une bonne chose, car sans espaces blancs, il était impossible de brouiller valeurs et arguments. En outre, il est facile de scinder une chaîne en deux sur le premier à partir du :caractère de gauche .

Mes questions sont:

  1. La -argument valueformule populaire est-elle meilleure que argument:value(plus lisible, plus facile à écrire, sans bug, plus facile à comprendre pour les développeurs experts)?
  2. Existe-t-il des règles connues que je devrais suivre lors de la conception des arguments en ligne de commande (autre que si ça marche, c'est OK)?

Demandé pour plus de détails je vais le fournir. Cependant, je pense qu'ils ne devraient pas affecter les réponses. La question concerne les 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 des lieux publics (totems tactiles, tableaux). Les applications sont écrites avec Qt Quick 5 (C ++, QML, JS). Windows 8.1 / 10 sera installé sur les périphériques. Nous allons fournir une interface frontale pour gérer les appareils. Cependant, certains administrateurs avancés peuvent vouloir configurer eux-mêmes l'application. Ce n'est pas très important du point de vue de l'entreprise, mais comme je suis d'accord avec ce que Kilian Foth a dit, je ne veux pas que mon application soit pénible pour un utilisateur. Ne trouvant pas dans Internet ce que je veux, j'ai demandé ici.


Aux utilisateurs plus avancés de Stack Exchange: Je voulais que cette question soit générale. Peut-être qualifie-t-il 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 constituer une leçon précieuse pour les autres développeurs.

Filip Hazubski
la source
14
Regardez les outils de ligne de commande populaires. Par exemple, le trait d'union est souvent utilisé pour permettre la combinaison d'options. par exemple, vous pourriez écrire ls -ltrpour combiner les options -l, -tet -r. Les programmes de style GNU autorisent également les options basées sur les mots avec un double trait d'union, --reverseau lieu de -r. Il existe d'autres conventions populaires comme -hafficher l'aide, --signaler la fin des options, spécifier -un nom de fichier pour autoriser la lecture à partir de stdin, etc.
Brandin
72
Ni sont -argument valuepas -argument:valuecommuns. Communs sont -a value, -avalueet --argument=value.
Reinierpost
39
Utilisez une bibliothèque d’analyse de ligne de commande populaire (généralement appelée quelque chose comme getopt(s)) où vous pouvez.
Reinierpost
5
@ k3b Nous travaillons avec Qt. Comme le dit kevin cline dans son commentaire, nous pouvons utiliser la bibliothèque déjà disponible. Je suppose que c'est multi-plateforme et bien pensé. QCommandLineParser
Filip Hazubski
11
Le problème avec votre texte final est que l'analyse des arguments n'est pas un problème indépendant de la plate-forme.
Pydsigner

Réponses:

237

Sur les systèmes POSIX (par exemple Linux, MacOSX), du 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 GNU (qui répertorie également les noms d'arguments courants) et de consulter les instructions relatives aux utilitaires POSIX. , même pour les logiciels propriétaires:

  • toujours manipuler --versionet--help (même les /bin/trueaccepte !!). Je maudis les auteurs de logiciels qui ne comprennent pas --help, je les déteste (car prog --help c'est la première commande que j'essaye d'un nouveau programme)! Souvent, --helppeut être abrégé en-h

  • Demandez au --helpmessage de répertorier toutes les options (sauf si vous en avez trop ... dans ce cas, listez les plus courantes et référez-vous explicitement à une manpage ou à une URL) et aux valeurs par défaut des options, et peut-être important (et spécifique au programme). ) Variables d'environnement. Afficher ces listes d'options en cas d'erreur d'argument.

  • accepter l' -aargument court (lettre simple) 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 --only-long-argumentnom; pour les arguments modaux sans options supplémentaires -cfest généralement traité comme -c -f, etc., donc votre -argument:valueproposition est étrange, et je ne recommande pas de le faire.

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

  • utilisez souvent -pour une entrée ou une sortie standard (si vous ne pouvez pas faire cela, manipulez /dev/stdinet /dev/stdoutmême sur les rares systèmes d'exploitation qui n'en ont pas)

  • imitez le comportement de programmes similaires en réutilisant la plupart de leurs conventions d'options; en particulier -npour les essais à sec make, -hà l'aide, -vpour la verbosité, etc ...

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

  • si votre programme utilise isattypour tester alors que stdin est un terminal (et se comporte de manière "interactive" dans ce cas), fournissez une option pour forcer le mode non interactif, de même si votre programme dispose d'une interface graphique (et effectue des tests 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, ce qui @somefile.txtsignifie que les arguments de programme lus sont lus somefile.txt; cela peut être utile lorsque votre programme peut accepter un très grand nombre d'arguments (plus que ceux de votre noyau ARG_MAX)

BTW, vous pouvez même ajouter des fonctionnalités de saisie automatique pour votre programme et les shells habituels (comme bashou 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 (à moins que vous ne leur donniez une meilleure variante).

Si votre logiciel est une série de programmes de ligne de commande connexes, se inspirer de git (que vous utilisez sûrement comme un outil de développement), qui accepte git helpet git --helpet ont beaucoup gitsubcommandetgitsubcommand--help

Dans de rares cas, vous pouvez également utiliser argv[0](en utilisant des liens symboliques dans votre programme), par exemple bashinvoqué sous rbashun comportement différent ( shell restreint ). Mais je ne recommande généralement pas de faire cela; il pourrait être judicieux d'utiliser votre programme comme interpréteur de script en utilisant shebang, c'est- #!à- dire sur la première ligne interprétée par execve (2) . Si vous faites de telles astuces, assurez-vous de les documenter, y compris dans les --helpmessages.

Rappelez - vous que sur la POSIX shell est englobement arguments ( avant l' exécution de votre programme!), Donc éviter d' exiger des caractères (comme *ou $ou ~) dans les options qui doivent être coquille échappé.

Dans certains cas, vous pouvez intégrer un interpréteur tel que GNU guile ou Lua dans votre logiciel (évitez d’inventer votre propre langage de script Turing-complete si vous n’êtes pas expert en langages de programmation). Cela a de profondes conséquences sur la conception de votre logiciel (il faut donc y penser tôt!). Vous devriez alors pouvoir facilement passer un script ou une expression à cet interprète. Si vous prenez cette approche intéressante, concevez votre logiciel et ses primitives interprétées avec soin; vous pourriez avoir un utilisateur étrange codant de gros scripts pour votre truc.

Dans d'autres cas, vous pouvez laisser vos utilisateurs avancés charger leur plug-in dans votre logiciel (en utilisant des techniques de chargement dynamiques à la dlopen& dlsym). Là encore, il s’agit d’une décision de conception très importante (définissez et documentez donc soigneusement l’interface du plug-in) et vous devrez définir une convention permettant de transmettre les options de programme à ces plug-ins.

Si votre logiciel est complexe, faites-le accepter certains fichiers de configuration (en plus du remplacement des arguments du programme) et possédez probablement un moyen de tester (ou tout simplement d’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 le "sécher à moitié" (par exemple, observer comment il gère une adresse email donnée sans envoyer réellement d'email).


Notez que le /optionest une chose Windows ou VMS. Ce serait insensé sur les systèmes POSIX (car la hiérarchie des fichiers est utilisée /comme séparateur de répertoires et parce que le shell effectue la suppression). Toute ma réponse est principalement pour Linux (et POSIX).


PS Si possible, faites de votre programme un logiciel libre , certains utilisateurs et développeurs vous apporteront des améliorations (et ajouter une nouvelle option à un programme est souvent l’une des choses les plus faciles à ajouter à un logiciel libre 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 du même nombre d’options qu’un compilateur, ou un inspecteur de réseau pour les systèmes de centre de traitement des données, ou un logiciel de CAO pour un microprocesseur. architectes ou pour les concepteurs de ponts. Un ingénieur familiarisé avec la programmation et les scripts aime probablement beaucoup plus que votre grand-mère d'avoir beaucoup d'options paramétrables, et voudra probablement pouvoir exécuter votre application sans X11 (peut-être dans un crontabtravail).

Basile Starynkevitch
la source
18
D'accord, mais git est un meilleur exemple. Je ne recommanderai même pas de regarder en cvs2016.
Basile Starynkevitch
8
+ en cas d'option non valide, affichez simplement l'aide. C'est SOOOO ennuyeux de recevoir un message d'erreur inutile par exemple pour dd -h.
Domen
8
Les programmes GNU supportent --helpgénéralement mais ne reconnaissent pas souvent ce -hqui est ennuyeux. Je tape habituellement -h quand j'oublie une option dans un programme, il est ennuyeux de devoir ressaisir la commande avec l'option la plus longue --help. Après tout, j'ai tapé -h parce que j'avais oublié quelque chose. Pourquoi devrait-on s’attendre à ce que l’utilisateur se souvienne des programmes qui nécessitent «--help» et des programmes qui nécessitent «-h» pour afficher l’écran d’aide? Incluez simplement une option pour -h et --help.
Brandin
30
La première partie de cette réponse est bonne, mais quelque part, vous vous en sortez un peu en tangente. Par exemple, les fichiers de configuration, les plugins et dlopen, les choix de licences logicielles et les navigateurs Web ne sont plus vraiment liés aux conventions d'une interface de ligne de commande.
Brandin
5
Et s'il vous plaît. Si vous formatez votre sortie pour écran, ajoutez un remplacement de format de sortie. Rien ne m'agace plus que des commandes qui tronquent ou qui enroulent des lignes lorsque je tente de les utiliser dans un script.
Sobrique
68

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

Vous pouvez facilement voir que l'utilisation de = ou: ou même '' comme séparateur est une différence triviale qui pourrait être convertie en un autre ordinateur sans effort. Qu'est-ce qui constituerait un gros effort pour un être humain à se rappeler "Maintenant, voyez-vous, ce programme rarement utilisé délimite-t-il les choses avec :ou avec =? Hmmm ..."

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

Kilian Foth
la source
19
pour presque toutes les langues, il existe des fonctions de bibliothèque permettant de gérer les arguments en ligne de commande. Utilisez l'un d'entre eux.
kevin cline
9
Bon conseil, mais quelles sont ces conventions? -1
RubberDuck
14
Il existe un exemple intéressant d'un outil UNIX couramment utilisé qui enfreint intentionnellement cette convention: ddutilise une key=valuesyntaxe. La raison de cette décision de conception est que cet outil (pseudo: d ata d estroyer) peut causer beaucoup de dommages s’il n’est pas utilisé correctement. En obligeant l'utilisateur à abandonner son habitude habituelle, il l'oblige à réfléchir de plus près à ce qu'il fait.
Philipp
18
Êtes-vous sûr que c'est la raison dd? Je crois qu'il a tout simplement été codé à un moment (dans les années 1970?) Où le fichier ARG_MAX du noyau était petit, les shells n'avaient pas d'auto-complétion et --long-argumentsn'existaient pas. Depuis lors, mieux ddest resté compatible avec les
versions antérieures
9
ddprovient d'un autre système d'exploitation (qu'UNIX) - un langage de script (JCL) sous IBM / 360 - où les conventions étaient différentes, avant d'être porté plus ou moins inchangé vers UNIX - en partie parce que ceux qui étaient susceptibles de l'utiliser système précédent.
Baard Kopperud
29

En termes simples

A Rome, fais comme les Romains.

  • Si votre application CLI est destinée à Linux / Unix, utilisez la convention -p valueou --parameter value. Linux dispose d'outils pour analyser facilement ces paramètres et ces indicateurs.

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 /flaget /flag:valueconventions.

  • Certaines applications comme Oracle n'utilisent ni l'une ni l'autre. Utilisation des utilitaires Oracle PARAMETER=VALUE.

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

  • Une suggestion supplémentaire consiste à autoriser l'utilisation de certaines variables d'environnement personnalisées . Par exemple, définir une variable d'environnement MYAPP_WRKSPACE=/tmprendrait inutile la définition de toujours --wrkspace /tmp.

  • Sous Linux, n'oubliez pas d'ajouter l' auto-complétion des paramètres , ce qui signifie que les utilisateurs peuvent taper la moitié d'un commutateur, cliquer TABet le shell le complétera pour eux.
Tulains Córdova
la source
1
Eh bien, plusieurs utilitaires GNU (par exemple gcc) gèrent @mifile.parcomme vos --parfile mifile.parsuggestions.
Basile Starynkevitch le
7
Êtes-vous sûr d'ignorer toutes les autres options s'il existe un fichier de paramètres? Cela me semble tout à fait contre-intuitif.
Jonathan Leffler
8
Je suis d'accord avec Jonathan sur les fichiers de paramètres. Si le fichier de paramètres est présent, je m'attendrais à ce qu'il soit utilisé pour les valeurs par défaut, avec les arguments donnés sur la ligne de commande appliqués par-dessus ceux du fichier parfile. Si, pour quelque raison que ce soit, l'utilisation de parfile empêche l'utilisation d'arguments de ligne de commande supplémentaires, la présence d'arguments supplémentaires doit être une erreur.
Eldritch Cheese
@EldritchCheese Dans les applications CLI que j'ai écrites, lorsqu'un paramètre parfile est fourni, tout paramètre additionnel génère une erreur.
Tulains Córdova
1
Si vous utilisez un shell capable, ce type d'option "paramètre de fichier de configuration" n'est pas nécessaire. Par exemple, vous écrivez simplement fooCmd -opt1 -opt2 $(cat more_options.opts). Par conséquent, je m'attendrais à ce qu'une option "paramètre de fichier de configuration", si elle est fournie, fonctionne fondamentalement de la même manière.
Brandin
19

Une chose qui n'est pas encore apparue:

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

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

Cela vous permettra de faire une analyse approfondie des cas critiques et des cas courants dès le début. Bien sûr, vous allez toujours faire abstraction de l'extérieur et de l'intérieur, mais cela donnera de bien meilleurs résultats que d'écrire tout le code et de claquer une CLI.

En outre, consultez docopt ( http://docopt.org/ ).

docopt est une aide précieuse dans de nombreuses langues, en particulier pour Python où les analyseurs d'arguments indésirables tels que argparse sont très limités et toujours considérés comme "OK". Au lieu d'avoir des analyseurs syntaxiques, des sous-vendeurs et des dictionnaires conditionnels, vous définissez simplement l' aide de la syntaxe et le reste.

Florian Heigl
la source
J'adore cette réponse et je vous en remercie, car je suis actuellement frustré de argparsene pas être programmeur, utilisateur ou convivial.
Chat
J'ai essayé docopt, et je n'aime pas ça. cliquez sur les résultats dans un code beaucoup plus propre, bien qu'il y ait un peu de malice avec des options lorsque vous avez des sous-commandes.
jpmc26
2
C'est trop comme une publicité. Mentionnez la ressource une seule fois si elle est pertinente. Mais dans l’état actuel des choses, 50% de votre réponse ressemble à une promotion de ressource externe.
Brandin le
Je le qualifierais de «concevez d'abord le modèle d'utilisation». Séparer l'expérience utilisateur de la fonctionnalité peut être une distinction artificielle dans de nombreux cas (les limitations d'outils affectent l'interface).
copper.hat
1
+1 pour Docopt. Cela a résolu tous mes dilemmes CLI, totalement sans douleur. Parfois , il est difficile de dire publicité de véritable enthousiasme, mais ici il est - je suis un passionné Docopt depuis des années, non affiliés quelque sorte;)
frnhr
3

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

Nous allons fournir une interface frontale pour gérer les appareils. Cependant, certains administrateurs avancés voudront peut-être configurer l'application eux-mêmes.

Mais aussi des remarques:

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

Vous devez tenir compte de votre public cible - les administrateurs avancés . Sur quelle plate-forme travaillent-ils normalement - Win / Unix / Mac? Et sur quelle plate-forme vous exécutez l'application? Suivez les conventions CLI déjà établies pour cette plate-forme. Est-ce que vos administrateurs "avancés" veulent / ont besoin d'un outil basé sur une interface graphique?

Vous voulez que l'interface soit cohérente en interne et avec les autres outils d'administration. Je ne veux pas m'arrêter et penser 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 ça cmd -p2 <arg> -p1 <arg>marche 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 Shelldorado de Heiner, ainsi que les autres références mentionnées.

Aussi important que de concevoir la CLI, vous devez 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 qu’aucune logique métier n’est utilisée dans l’interface graphique ou que vous utilisez la commande commune appelée à partir de la GUI et de la CLI.

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

Tcl / Tk est le jeu d’outils classique utilisé avec cette approche . Ne pas suggérer de changer d’outil; il suffit de considérer une approche de conception consistant à écrire une application administrative basée sur une interface graphique pour la première fois en tant qu'outil de ligne de commande; superposez ensuite l'interface graphique pour plus de commodité. À un moment donné, vous découvrirez probablement que l'interface graphique est un problème (et source d'erreurs) si vous devez effectuer plusieurs configurations et ressaisir les mêmes options à plusieurs reprises et que vous rechercherez une approche automatisée.

N'oubliez pas que votre administrateur doit probablement saisir les valeurs correctes dans les zones correctes de toute façon, alors combien d'efforts relâchez-vous quand même avec l'interface graphique?

Ian W
la source
Génial, oui! Une question est également: puis-je exécuter ./this-script --hosts <hosts.txt
Florian Heigl