108 votes

Stratégie de transmission d'arguments - variables d'environnement vs ligne de commande

La plupart des applications que nous développons en tant que développeurs doivent être paramétrées au démarrage. Nous passons des chemins de fichiers, des noms de tuyaux, des adresses TCP/IP, etc. Jusqu'à présent, j'utilisais la ligne de commande pour transmettre ces paramètres à l'application lancée. Je devais analyser la ligne de commande dans main et diriger les arguments là où ils étaient nécessaires, ce qui est bien sûr une bonne conception, mais est difficile à maintenir pour un grand nombre d'arguments. Récemment, j'ai décidé d'utiliser le mécanisme des variables d'environnement. Elles sont globales et accessibles de n'importe où, ce qui est moins élégant d'un point de vue architectural, mais limite la quantité de code.

Ce sont mes premières impressions (et peut-être assez superficielles) sur les deux stratégies, mais j'aimerais entendre l'avis de développeurs plus expérimentés - Quels sont les avantages et les inconvénients de l'utilisation des variables d'environnement et des arguments de ligne de commande pour transmettre des arguments à un processus? J'aimerais prendre en compte les aspects suivants:

  1. qualité de la conception (flexibilité/maintenabilité),
  2. contraintes de mémoire,
  3. portabilité de la solution.

Remarques:

Ad. 1. Il s'agit de l'aspect principal qui m'intéresse.

Ad. 2. C'est un peu pragmatique. Je connais certaines limitations sur Windows qui sont actuellement énormes (plus de 32 ko pour la ligne de commande et le bloc d'environnement). Je suppose que ce n'est pas un problème cependant, car vous devriez simplement utiliser un fichier pour transmettre des tonnes d'arguments si nécessaire.

Ad. 3. Je ne connais presque rien de Unix, donc je ne suis pas sûr si les deux stratégies sont aussi utilisables de manière similaire que sur Windows. Développez là-dessus s'il vous plaît.

99voto

Matt Fenwick Points 17097

1) Je recommande d'éviter autant que possible les variables d'environnement.

Avantages des variables d'environnement

  • faciles à utiliser car visibles de n'importe où. Si de nombreux programmes indépendants ont besoin d'une information, cette approche est beaucoup plus pratique.

Inconvénients des variables d'environnement

  • difficiles à utiliser correctement car visibles (supprimables, modifiables) de n'importe où. Si j'installe un nouveau programme qui dépend de variables d'environnement, vont-elles écraser celles existantes? Ai-je involontairement corrompu mes variables d'environnement en bricolant hier?

Mon opinion

  • utilisez des arguments en ligne de commande pour les arguments qui sont les plus susceptibles de changer pour chaque invocation individuelle du programme (par exemple, n pour un programme qui calcule n!)
  • utilisez des fichiers de configuration pour les arguments qu'un utilisateur pourrait raisonnablement vouloir changer, mais pas très souvent (par exemple, la taille de l'affichage lorsque la fenêtre s'ouvre)
  • utilisez les variables d'environnement avec parcimonie - de préférence uniquement pour les arguments qui ne sont pas censés changer (par exemple, l'emplacement de l'interpréteur Python)
  • votre point Ils sont globaux et accessibles de n'importe où, ce qui est moins élégant d'un point de vue architectural, mais limite la quantité de code me rappelle les justifications pour l'utilisation de variables globales ;)

Mes cicatrices après avoir vécu de première main les horreurs de l'utilisation excessive de variables d'environnement

  • deux programmes dont nous avons besoin au travail, qui ne peuvent pas fonctionner sur le même ordinateur en même temps en raison de conflits environnementaux
  • plusieurs versions de programmes avec le même nom mais différents bugs - ont mis un atelier entier à genoux pendant des heures car l'emplacement du programme était extrait de l'environnement, et était (silencieusement, subtilement) incorrect.

2) Limites

Si j'atteignais les limites de ce que la ligne de commande peut contenir, ou de ce que l'environnement peut gérer, je refactoriserais immédiatement.

J'ai utilisé JSON dans le passé pour une application en ligne de commande qui avait besoin de nombreux paramètres. Il était très pratique de pouvoir utiliser des dictionnaires et des listes, ainsi que des chaînes de caractères et des nombres. L'application n'avait que quelques arguments en ligne de commande, dont l'un était l'emplacement du fichier JSON.

Avantages de cette approche

  • n'avais pas besoin d'écrire beaucoup de code (douloureux) pour interagir avec une bibliothèque CLI - il peut être difficile d'amener de nombreuses bibliothèques courantes à imposer des contraintes compliquées (par 'compliquées', je veux dire plus complexes que la vérification d'une clé spécifique ou l'alternance entre un ensemble de clés)
  • ne pas avoir à se soucier des exigences des bibliothèques CLI pour l'ordre des arguments - simplement utiliser un objet JSON!
  • facile à représenter des données complexes (répondant à Qu'est-ce qui ne rentrera pas dans les paramètres de la ligne de commande?) telles que des listes
  • facile à utiliser les données d'autres applications - à la fois pour les créer et les analyser de manière programmable
  • facile à accommoder des futures extensions

Note : Je veux distinguer cela de l'approche du fichier .config - ce n'est pas pour stocker la configuration utilisateur. Peut-être devrais-je appeler cela l'approche du 'fichier de paramètres en ligne de commande', car je l'utilise pour un programme qui a besoin de nombreuses valeurs qui ne s'inscrivent pas bien sur la ligne de commande.


3) Portabilité de la solution : Je ne connais pas grand-chose sur les différences entre Mac, PC et Linux en ce qui concerne les variables d'environnement et les arguments de ligne de commande, mais je peux vous dire :

  • les trois prennent en charge les variables d'environnement
  • ils prennent tous en charge les arguments de ligne de commande

Oui, je sais - ce n'était pas très utile. Je suis désolé. Mais le point clé est que vous pouvez vous attendre à ce qu'une solution raisonnable soit portable, bien que vous voudriez certainement vérifier cela pour vos programmes (par exemple, les arguments de ligne de commande sont-ils sensibles à la casse sur certaines plates-formes ? sur toutes les plates-formes ? Je ne sais pas).


Un dernier point :

Comme l'a mentionné Tomasz, cela ne devrait pas importuner la plupart des applications d'où viennent les paramètres.

12voto

Tomasz Nurkiewicz Points 140462

Vous devriez abstraire la lecture des paramètres en utilisant le pattern Stratégie. Créez une abstraction nommée ConfigurationSource ayant la méthode readConfig(key) -> value (ou retournant un objet/structure Configuration) avec les implémentations suivantes :

  • CommandLineConfigurationSource
  • EnvironmentVariableConfigurationSource
  • WindowsFileConfigurationSource - chargement à partir d'un fichier de configuration de C:/Document et paramètres...
  • WindowsRegistryConfigurationSource
  • NetworkConfigrationSource
  • UnixFileConfigurationSource - chargement à partir d'un fichier de configuration de /home/utilisateur/...
  • DefaultConfigurationSource - valeurs par défaut
  • ...

Vous pouvez également utiliser le pattern Chain of responsibility pour chaîner les sources dans différentes configurations telles que : si un argument de ligne de commande n'est pas fourni, essayer les variables d'environnement et si tout le reste échoue, retourner les valeurs par défaut.

Ad 1. Cette approche vous permet non seulement d'abstraire la lecture de la configuration, mais vous pouvez également changer le mécanisme sous-jacent sans aucun impact sur le code client. De plus, vous pouvez utiliser plusieurs sources à la fois, en faisant des sauvegardes ou en rassemblant la configuration à partir de différentes sources.

Ad 2. Choisissez simplement l'implémentation appropriée. Bien sûr, certaines entrées de configuration ne conviendront pas, par exemple, aux arguments de ligne de commande.

Ad 3. Si certaines implémentations ne sont pas portables, en avoir deux, une ignorée/skippée silencieusement lorsqu'elle n'est pas adaptée à un système donné.

7voto

mdornfe1 Points 682

Je pense que cette question a été plutôt bien répondue déjà, mais je pense qu'elle mérite une mise à jour en 2018. Je pense qu'un avantage non mentionné des variables d'environnement est qu'elles nécessitent généralement moins de code de base à travailler. Cela permet d'avoir un code plus propre et plus lisible. Cependant, un inconvénient majeur est qu'elles suppriment des couches d'isolation entre différentes applications s'exécutant sur la même machine. Je pense que c'est là que Docker brille vraiment. Mon modèle de conception préféré est d'utiliser exclusivement des variables d'environnement et d'exécuter l'application à l'intérieur d'un conteneur Docker. Cela résout le problème d'isolation.

3voto

Je suis généralement d'accord avec les réponses précédentes, mais il y a un autre aspect important: la facilité d'utilisation.

Par exemple, dans git, vous pouvez créer un dépôt avec le répertoire .git en dehors de celui-ci. Pour spécifier cela, vous pouvez utiliser un argument de ligne de commande --git-dir ou une variable d'environnement GIT_DIR.

Bien sûr, si vous changez le répertoire actuel vers un autre dépôt ou héritez des variables d'environnement dans des scripts, vous obtenez une erreur. Mais si vous devez taper plusieurs commandes git dans un dépôt détaché pendant une session de terminal, c'est extrêmement pratique : vous n'avez pas besoin de répéter l'argument git-dir.

Un autre exemple est GIT_AUTHOR_NAME. Il semble qu'il n'ait même pas de partenaire en ligne de commande (cependant, git commit a un argument --author). GIT_AUTHOR_NAME remplace les paramètres de configuration user.name et author.name.

En général, l'utilisation d'arguments en ligne de commande ou environnementaux est tout aussi simple sur UNIX : on peut utiliser un argument de ligne de commande

$ commande --arg=monarg

ou une variable d'environnement en une seule ligne :

$ ARG=monarg commande

Il est également facile de capturer des arguments de ligne de commande dans un alias :

alias cfg='git --git-dir=$HOME/.cfg/ --work-tree=$HOME'  # pour les fichiers de configuration
alias grep='grep --color=auto'

En général, la plupart des arguments sont passés par la ligne de commande. Je suis d'accord avec les réponses précédentes, selon lesquelles cela est plus fonctionnel et direct, et que les variables d'environnement dans les scripts sont comme des variables globales dans les programmes.

GNU libc dit ceci :

Le mécanisme argv est généralement utilisé pour passer des arguments de ligne de commande spécifiques au programme particulier qui est invoqué. En revanche, l'environnement garde une trace des informations partagées par de nombreux programmes, qui changent rarement et qui sont moins souvent utilisées.

Outre ce qui a été dit sur les dangers des variables d'environnement, il existe de bons cas d'utilisation pour celles-ci. GNU make gère de manière très flexible les variables d'environnement (et est donc très intégré au shell) :

Chaque variable d'environnement que make voit au démarrage est transformée en une variable make portant le même nom et la même valeur. Cependant, une affectation explicite dans le fichier makefile, ou avec un argument de commande, remplace l'environnement. (-- et il y a une option pour changer ce comportement) ...

Ainsi, en définissant la variable CFLAGS dans votre environnement, vous pouvez faire en sorte que toutes les compilations C dans la plupart des makefiles utilisent les commutateurs du compilateur que vous préférez. Cela est sûr pour les variables avec des significations standard ou conventionnelles car vous savez qu'aucun fichier make ne les utilisera pour d'autres choses.

Enfin, je voudrais souligner que ce qui est le plus important pour un programme n'est pas le programmeur, mais l'expérience utilisateur. Peut-être avez-vous inclus cela dans l'aspect design, mais le design interne et externe sont des entités assez différentes.

Et quelques mots sur les aspects de la programmation. Vous n'avez pas mentionné le langage que vous utilisez, mais imaginons que vos outils vous permettent l'analyse des arguments la plus optimale possible. En Python, j'utilise argparse, qui est très flexible et riche. Pour obtenir les arguments analysés, on peut utiliser une commande comme

args = parser.parse_args()

args peut être ensuite divisé en arguments analysés (disons args.my_option), mais je peux aussi les passer dans leur ensemble à ma fonction. Cette solution n'est absolument pas "difficile à maintenir pour un grand nombre d'arguments" (si votre langage le permet). En effet, si vous avez de nombreux paramètres et qu'ils ne sont pas utilisés lors de l'analyse des arguments, passez-les dans un conteneur vers leur destination finale et évitez la duplication de code (ce qui conduit à l'inflexibilité).

Et enfin, il est beaucoup plus facile de parser des variables d'environnement que des arguments de ligne de commande. Une variable d'environnement est simplement une paire, VARIABLE=valeur. Les arguments de ligne de commande peuvent être beaucoup plus complexes : ils peuvent être des arguments positionnels ou par mot-clé, ou des sous-commandes (comme git push). Ils peuvent capturer zéro ou plusieurs valeurs (pensez à la commande echo et aux drapeaux comme -vvv). Voir argparse pour plus d'exemples.

Et une dernière chose. Votre préoccupation concernant la mémoire est un peu perturbante. Ne créez pas de programmes surgénéraux. Une bibliothèque doit être flexible, mais un bon programme est utile sans aucun argument. Si vous devez en passer beaucoup, il s'agit probablement de données, pas d'arguments. Comment lire des données dans un programme est une question beaucoup plus générale sans solution unique pour tous les cas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X