参数传递策略 - 环境变量与命令行

时间:2011-09-16 10:30:58

标签: command-line process environment-variables argument-passing spawn

我们开发人员编写的大多数应用程序都需要在启动时进行外部参数化。我们传递文件路径,管道名称,TCP / IP地址等。到目前为止,我一直在使用命令行将这些传递给正在启动的应用程序。我必须解析main中的命令行,并将参数指向他们需要的地方,这当然是好的设计,但难以维护对于大量的论点。最近我决定使用环境变量机制。它们是全局的,可以从任何地方访问,从架构的角度来看,不太优雅,但限制了代码量

这是我对这两种策略的第一次(也可能是非常浅薄的)印象,但我想听听更有经验的开发人员的意见 - 使用环境变量和命令行参数的起伏有哪些流程的论据?我想考虑以下事项:

  1. 设计质量(灵活性/可维护性),
  2. 内存限制,
  3. 解决方案可移植性。

  4. 说明:

    广告。这是我感兴趣的主要方面。

    广告。这有点务实。我知道Windows上有一些限制,目前huge(命令行和环境块都超过32kB)。我想这不是问题,因为如果需要,你应该使用一个文件来传递大量的参数。

    广告。我几乎不知道Unix,所以我不确定这两种策略是否像在Windows上一样可用。如果你愿意,请详细说明。

4 个答案:

答案 0 :(得分:63)

1)我建议尽可能避免使用环境变量。

环境变量的优点

  • 易于使用,因为它们可以从任何地方看到。如果许多独立程序需要一条信息,这种方法会更方便。

环境变量的缺点

  • 难以正确使用,因为它们可以从任何地方看到(可删除,可设置)。如果我安装一个依赖环境变量的新程序,他们会踩到我现有的吗?昨天我在周围逛逛时,是否无意中搞砸了我的环境变量?

我的意见

  • 对于那些对程序的每次单独调用最有可能不同的参数使用命令行参数(即n为计算n的程序!)
  • 将配置文件用于用户可能合理想要更改的参数,但不经常使用(即弹出窗口时的显示大小)
  • 谨慎使用环境变量 - 最好只用于预期不会改变的参数(即Python解释器的位置)
  • 你的观点They are global and accessible from anywhere, which is less elegant from architectural point of view, but limits the amount of code提醒我使用全局变量的理由;)

我亲身体验环境变量过度使用恐怖的伤疤

  • 我们工作所需的两个程序,由于环境冲突而无法同时在同一台计算机上运行
  • 具有相同名称但不同错误的程序的多个版本 - 使整个研讨会陷入困境数小时,因为程序的位置已从环境中拉出,并且(默默地,巧妙地)错误。

2)限制

如果我正在推动命令行可以容纳的限制,或者环境可以处理什么,我会立即重构。

我过去曾使用JSON作为需要大量参数的命令行应用程序。能够使用字典和列表以及字符串和数字非常方便。该应用程序只使用了几个命令行参数,其中一个是JSON文件的位置。

此方法的优点

  • 没有必要写很多(痛苦的)代码来与CLI库进行交互 - 让许多常见的库强制执行复杂的约束可能会很麻烦(“复杂”我的意思是比检查特定键或一组键之间的交替)
  • 不必担心CLI库对参数顺序的要求 - 只需使用JSON对象!
  • 易于表示复杂数据(回答What won't fit into command line parameters?),例如列表
  • 易于使用来自其他应用程序的数据 - 以编程方式创建和解析
  • 轻松容纳未来的扩展

注意:我想区别于.config文件方法 - 这不是用于存储用户配置。也许我应该将其称为'命令行参数文件'方法,因为我将它用于需要许多不适合命令行的值的程序。


3)解决方案的可移植性:我不太了解Mac,PC和Linux在环境变量和命令行参数方面的差异,但我可以告诉你:

  • 这三个人都支持环境变量
  • 他们都支持命令行参数

是的,我知道 - 这不是很有帮助。对不起。但关键是你可以期望合理的解决方案是可移植的,尽管你肯定想要为你的程序验证这一点(例如,命令行args是否在任何平台上都是区分大小写的?平台?我不知道。)


最后一点:

正如Tomasz所提到的,对于参数来源的大部分应用来说都无关紧要。

答案 1 :(得分:5)

您应该使用Strategy模式抽象阅读参数。使用ConfigurationSource方法(或返回一些readConfig(key) -> value对象/结构)创建名为Configuration的抽象,并实现以下实现:

  • CommandLineConfigurationSource
  • EnvironmentVariableConfigurationSource
  • WindowsFileConfigurationSource - 从C:/Document and settings...
  • 的配置文件加载
  • WindowsRegistryConfigurationSource
  • NetworkConfigrationSource
  • UnixFileConfigurationSource - 从/home/user/...
  • 的配置文件加载
  • DefaultConfigurationSource - 默认
  • ...

您还可以使用Chain of responsibility模式在各种配置中链接源:如果未提供命令行参数,请尝试环境变量,如果其他所有操作都失败,则返回默认值。

广告1.此方法不仅允许您抽象阅读配置,还可以轻松更改基础机制,而不会对客户端代码产生任何影响。此外,您可以同时使用多个来源,退回或从不同来源收集配置。

广告2.只需选择适合的实施方式即可。当然,一些配置条目不适合例如命令行参数。

广告3.如果某些实现不可移植,则在不适合给定系统时,有两个,一个默默地忽略/跳过。

答案 2 :(得分:2)

我认为这个问题已经得到了很好的回答,但我觉得它应该得到2018年的更新。我觉得环境变量的一个未提及的好处是它们通常需要较少的锅炉板代码才能使用。这使代码更清晰,更易读。然而,一个主要的缺点是他们从同一台机器上运行的不同应用程序中删除了一层隔离。我认为这是Docker真正闪耀的地方。我最喜欢的设计模式是专门使用环境变量并在Docker容器中运行应用程序。这消除了隔离问题。

答案 3 :(得分:0)

我大体上同意之前的回答,但还有一个重要方面:可用性

例如,在 git 中,您可以使用 .git 目录之外的目录创建存储库。要指定它,您可以使用命令行参数 --git-dir 或环境变量 GIT_DIR

当然,如果将当前目录更改为另一个存储库或继承脚本中的环境变量,则会出错。但是,如果您需要在一个终端会话中在分离的存储库中键入多个 git 命令,这非常方便:您不需要重复 git-dir 参数。

另一个例子是GIT_AUTHOR_NAME。似乎它甚至没有命令行伙伴(但是,git commit 有一个 --author 参数)。 GIT_AUTHOR_NAME 覆盖 user.nameauthor.name 配置设置。

通常,在 UNIX 上使用命令行或环境参数同样简单:可以使用命令行参数

$ command --arg=myarg

或一行中的环境变量:

$ ARG=myarg command

alias 中捕获命令行参数也很容易:

alias cfg='git --git-dir=$HOME/.cfg/ --work-tree=$HOME'  # for dotfiles
alias grep='grep --color=auto'

一般来说,大多数参数都是通过命令行传递的。我同意之前的回答,这更实用,更直接,脚本中的环境变量就像程序中的全局变量。

GNU libc 是这样说的:

<块引用>

argv 机制通常用于传递特定于正在调用的特定程序的命令行参数。另一方面,环境会跟踪许多程序共享的信息,这些信息很少更改,也不太经常使用。

除了所说的环境变量的危险之外,还有一些很好的用例。 GNU make 对环境变量的处理非常灵活(因此与 shell 非常集成):

<块引用>

make 启动时看到的每个环境变量都被转换为具有相同名称和值的 make 变量。但是,makefile 中的显式赋值或带有命令参数的赋值会覆盖环境。 (-- 并且有一个选项可以改变这种行为) ...

<块引用>

因此,通过在您的环境中设置变量 CFLAGS,您可以使大多数 makefile 中的所有 C 编译使用您喜欢的编译器开关。这对于具有标准或常规含义的变量是安全的,因为您知道没有 makefile 会将它们用于其他用途。

最后,我要强调一个程序最重要的不是程序员,而是用户体验。也许您将其包含在设计方面,但内部和外部设计是完全不同的实体。

关于编程方面的几句话。您没有编写您使用的语言,但让我们想象一下您的工具允许您进行最佳参数解析。在 Python 中我使用 argparse,它非常灵活和丰富。要获取解析的参数,可以使用像

这样的命令
args = parser.parse_args()

args 可以进一步拆分为解析的参数(比如 args.my_option),但我也可以将它们作为一个整体传递给我的函数。此解决方案绝对不是“难以维护大量参数”(如果您的语言允许)。确实,如果您有很多参数并且在参数解析期间没有使用它们,请将它们放在容器中传递到它们的最终目的地,并避免代码重复(这会导致不灵活)。

最后的评论是解析环境变量比解析命令行参数要容易得多。环境变量只是一对,VARIABLE=value。命令行参数可能要复杂得多:它们可以是位置参数或关键字参数,或子命令(如 git push)。它们可以捕获零个或多个值(回忆命令 echo-vvv 之类的标志)。有关更多示例,请参阅 argparse

还有一件事。你对记忆的担忧有点令人不安。不要编写过于通用的程序。一个库应该是灵活的,但一个好的程序是有用的,没有任何参数。如果您需要传递很多信息,这可能是数据,而不是参数。如何将数据读入程序是一个更普遍的问题,没有适用于所有情况的单一解决方案。