我们开发人员编写的大多数应用程序都需要在启动时进行外部参数化。我们传递文件路径,管道名称,TCP / IP地址等。到目前为止,我一直在使用命令行将这些传递给正在启动的应用程序。我必须解析main
中的命令行,并将参数指向他们需要的地方,这当然是好的设计,但难以维护对于大量的论点。最近我决定使用环境变量机制。它们是全局的,可以从任何地方访问,从架构的角度来看,不太优雅,但限制了代码量。
这是我对这两种策略的第一次(也可能是非常浅薄的)印象,但我想听听更有经验的开发人员的意见 - 使用环境变量和命令行参数的起伏有哪些流程的论据?我想考虑以下事项:
说明:
广告。这是我感兴趣的主要方面。
广告。这有点务实。我知道Windows上有一些限制,目前huge(命令行和环境块都超过32kB)。我想这不是问题,因为如果需要,你应该使用一个文件来传递大量的参数。
广告。我几乎不知道Unix,所以我不确定这两种策略是否像在Windows上一样可用。如果你愿意,请详细说明。
答案 0 :(得分:63)
1)我建议尽可能避免使用环境变量。
环境变量的优点
环境变量的缺点
我的意见
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文件的位置。
此方法的优点
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.name 和 author.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。
还有一件事。你对记忆的担忧有点令人不安。不要编写过于通用的程序。一个库应该是灵活的,但一个好的程序是有用的,没有任何参数。如果您需要传递很多信息,这可能是数据,而不是参数。如何将数据读入程序是一个更普遍的问题,没有适用于所有情况的单一解决方案。