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


76

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

这是我对这两种策略的第一(也许很浅)的印象,但是我想听听更有经验的开发人员的意见-使用环境变量和命令行参数将参数传递给流程有什么起伏?我想考虑以下事项:

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

备注:

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

广告。2.这有点实用。据我所知,这是目前在Windows上一定的局限性巨大(超过32kB的两个命令行和环境块)。我想这不是问题,因为您只需要使用文件来传递大量参数即可。

广告。3.我对Unix几乎一无所知,所以我不确定这两种策略是否与Windows一样可用。如果需要,请详细说明。


您是否会提供更多细节,例如实际的参数数量?是否有分组或它们都是随机的?这是什么语言?Java,C ++等。我要求提供这种详细信息的原因是,尽管用任何一种语言来处理都可能是一个问题,但是您可能还没有意识到特定于语言实现的解决方案。
詹姆斯·丹纳德

仅提到* nix操作系统,它们就没有“全局环境变量”之类的东西,并且每个env var都是在派生时间从父进程继承到子进程的。因此,“ global”不是命令行环境的专家,至少对于那些OS而言不是这样。
shr

嗨,@ jamesDrinkard。我对一般方法感兴趣。如果要将20个不同的带标签的字符串/整数/实数参数从32位解释器运行的Python脚本传递给用C ++编写的64位应用程序,您将使用哪种方法?
Janusz Lenar

嗨,@ shr。谢谢您的* nix注意。正如雷蒙德(Raymond)在下面指出的那样,对于这项任务而言,这种全球化根本不是专家。
Janusz Lenar

1
这可能是相关的并且提倡环境变量:devcenter.heroku.com/articles/config-vars
eyeApps LLC

Answers:


77

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库进行交互-要使许多通用库强制执行复杂的约束可能会很痛苦(“复杂”是指比检查a更为复杂)特定键或一组键之间的交替)
  • 不必担心CLI库对参数顺序的要求-只需使用JSON对象即可!
  • 易于表示复杂数据(What won't fit into command line parameters?如清单)
  • 易于使用其他应用程序中的数据-以编程方式创建和解析
  • 易于容纳将来的扩展

注意:我想将此与.config-file方法区分开-这不是用于存储用户配置。也许我应该将其称为“命令行参数文件”方法,因为我将其用于需要大量在命令行上不太适合的值的程序。


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

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

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


最后一点:

正如Tomasz所提到的,对于大多数应用程序来说,参数的来源并不重要。


谢谢你,马特。这是我一直在寻找的一种意见。您最重要的建议是使用环境变量进行执行环境描述(几乎不变),使用cmd-file进行实际执行简单/复杂参数传递。非常理性,谢谢。请注意,尽管您可以使用“本地”环境变量,但这只会弄乱子进程。除了Raymond在Tomasz的回答下指出的内容外,这与传递命令行参数非常相似。
Janusz Lenar

1
很好的答案!关于可以从任何地方更改环境变量的缺点:还可以从应用程序的启动脚本(例如Bash或Batch脚本)本地设置环境变量。在这种情况下,可以有系统范围的默认值,但是应用程序可以根据需要将默认值更改为自定义值。您对此有何看法?
2015年

在考虑如何传递秘密/凭证时,有什么利弊?
iamyojimbo

我同意桌面和CLI应用程序。对于具有许多部署的云系统,env变量是一个很好的选择,例如在12factor指南中推荐:12factor.net/config
Lars Grammel

7

您应该使用策略模式来抽象读取参数。使用以下实现创建一个名为ConfigurationSource具有readConfig(key) -> value方法(或返回一些Configuration对象/结构)的抽象:

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

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

广告1.此方法不仅允许您抽象读取配置,而且可以轻松更改基础机制,而不会影响客户端代码。另外,您可以一次使用多个资源,可以回退或从其他资源收集配置。

广告2。只需选择合适的实施方式即可。当然,某些配置条目将不适用于命令行参数。

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


谢谢,这通常是个好主意。但这对于决定使用环境还是命令行没有帮助。详细说明Ad.2。的“某些配置项例如不适合命令行参数”将很有帮助。什么不适合字符串?如果不适合,则可能应该以某种文件形式间接传递它,不是吗?
Janusz Lenar

我的观点是:不要强迫用户使用命令行参数环境变量。保持灵活性(并保留可维护的代码)。我认为配置文件是存储配置的最佳位置(它可以任意长,包含注释等),但是有时使用命令行参数覆盖文件配置很有用。什么不适合命令行参数?如果您需要传递多个文件路径,则可能会起作用,但是没人喜欢过长的命令行。
Tomasz Nurkiewicz

配置文件最适合用作参数-这是有价值的意见,对注释的支持是使用它的一个很好的理由,谢谢。如果从批处理脚本启动应用程序时使用环境变量,则可以使用rem和编写具有可读性的表格set。如果要生成一个进程,则setenvspawnl-ing之前就可以执行所需的操作。它方便,易读且灵活。为什么要使用.config而不是环境?就是那个问题。
Janusz Lenar

3
注意环境变量是继承的。假设您的程序有两个参数ACTION和一个可选参数NOTIFY。程序A设置ACTION=if owner=nobody set owner=bobNOTIFY=send运行程序。您的程序将更新一个项目,然后查看NOTIFY已设置并运行send。该send程序将电子邮件发送给Bob,然后再次运行您的程序,设置为ACTION=set last_send = today。它不需要任何通知,因此不设置NOTIFY。但是它继承 NOTIFY自程序A,因此您的程序将最后一次运行更新为今天,然后运行send。无限循环。
Raymond Chen

1
谢谢@Raymond。环境变量的范围非常危险。好点子。
Janusz Lenar

4

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.