如何覆盖 Perl 程序中的硬编码配置?

发布于 2024-07-10 06:18:24 字数 2081 浏览 7 评论 0原文

我有一个 Perl 脚本,它在顶部附近为它将使用的目录和文件设置变量。 它还需要将一些变量设置为命令行参数。 示例:

use Getopt::Long;

my ($mount_point, $sub_dir, $database_name, $database_schema);
# Populate variables from the command line:
GetOptions(
    'mount_point=s'       => \$mount_point,
    'sub_dir=s'           => \$sub_dir,
    'database_name=s'     => \$database_name,
    'database_schema=s'   => \$database_schema
);
# ...  validation of required arguments here

################################################################################
# Directory variables
################################################################################
my $input_directory    = "/${mount_point}/${sub_dir}/input";
my $output_directory   = "/${mount_point}/${sub_dir}/output";
my $log_directory      = "/${mount_point}/${sub_dir}/log";
my $database_directory = "/db/${database_name}";
my $database_scripts   = "${database_directory}/scripts";

################################################################################
# File variables
################################################################################
my $input_file       = "${input_dir}/input_file.dat";
my $output_file      = "${output_dir}/output_file.dat";
# ... etc

这在我的开发、测试和生产环境中运行良好。 但是,我试图更轻松地覆盖某些变量(无需进入调试器)以进行开发和测试。 (例如,如果我想设置我的 input_file =“/tmp/my_input_file.dat”)。 我的想法是使用 GetOptions 函数来处理这个问题,如下所示:

GetOptions(
    'input_directory=s'      => \$input_directory,
    'output_directory=s'     => \$output_directory,
    'database_directory=s'   => \$database_directory,
    'log_directory=s'        => \$log_directory,
    'database_scripts=s'     => \$database_scripts,
    'input_file=s'           => \$input_file,
    'output_file=s'          => \$output_file
);

GetOptions 只能调用一次(据我所知)。 我的第一个片段中的前 4 个参数是必需的,上面的最后 7 个参数是可选的。 我认为理想的情况是像我的第一个代码片段中那样设置默认值,然后以某种方式覆盖在命令行传递参数时已设置的任何默认值。 我考虑过将所有选项存储在哈希中,然后在使用默认值设置每个变量时使用该哈希,除非哈希中存在条目,但这似乎添加了很多额外的逻辑。 有没有办法在脚本中的两个不同位置调用 GetOptions ?

不确定这是否有意义。

谢谢!

I have a Perl script that sets up variables near the top for directories and files that it will use. It also requires a few variables to be set as command-line arguments.
Example:

use Getopt::Long;

my ($mount_point, $sub_dir, $database_name, $database_schema);
# Populate variables from the command line:
GetOptions(
    'mount_point=s'       => \$mount_point,
    'sub_dir=s'           => \$sub_dir,
    'database_name=s'     => \$database_name,
    'database_schema=s'   => \$database_schema
);
# ...  validation of required arguments here

################################################################################
# Directory variables
################################################################################
my $input_directory    = "/${mount_point}/${sub_dir}/input";
my $output_directory   = "/${mount_point}/${sub_dir}/output";
my $log_directory      = "/${mount_point}/${sub_dir}/log";
my $database_directory = "/db/${database_name}";
my $database_scripts   = "${database_directory}/scripts";

################################################################################
# File variables
################################################################################
my $input_file       = "${input_dir}/input_file.dat";
my $output_file      = "${output_dir}/output_file.dat";
# ... etc

This works fine in my dev, test, and production environments. However, I was trying to make it easier to override certain variables (without going into the debugger) for development and testing. (For example, if I want to set my input_file = "/tmp/my_input_file.dat"). My thought was to use the GetOptions function to handle this, something like this:

GetOptions(
    'input_directory=s'      => \$input_directory,
    'output_directory=s'     => \$output_directory,
    'database_directory=s'   => \$database_directory,
    'log_directory=s'        => \$log_directory,
    'database_scripts=s'     => \$database_scripts,
    'input_file=s'           => \$input_file,
    'output_file=s'          => \$output_file
);

GetOptions can only be called once (as far as I know). The first 4 arguments in my first snippit are required, the last 7 directly above are optional. I think an ideal situation would be to setup the defaults as in my first code snippit, and then somehow override any of them that have been set if arguments were passed at the command line. I thought about storing all my options in a hash and then using that hash when setting up each variable with the default value unless an entry exists in the hash, but that seems to add a lot of additional logic. Is there a way to call GetOptions in two different places in the script?

Not sure if that makes any sense.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

瑾兮 2024-07-17 06:18:24

听起来您需要更改程序以使用配置文件而不是硬编码配置。 我在《掌握 Perl》中用了一整章的内容来阐述这一点。 您不想更改源代码来测试程序。

CPAN 上有许多 Perl 模块,使配置文件成为一个易于添加的功能。 选择最适合您的输入数据的一种。

一旦有了更好的配置模型,您就可以轻松设置默认值,从多个位置(文件、命令行等)获取值,并使用不同的值轻松测试程序。

It sounds like you need to change your program to use configuration files rather than hard-coded configuration. I devoted an entire chapter of Mastering Perl to this. You don't want to change source code to test the program.

There are many Perl modules on CPAN that make configuration files an easy feature to add. Choose the one that works best for your input data.

Once you get a better configuration model in place, you can easily set default values, take values from multiple places (files, command-line, etc), and easily test the program with different values.

彻夜缠绵 2024-07-17 06:18:24

这是另一种方法。 它使用名称数组和哈希来存储选项。 它使所有选项真正成为可选选项,但会验证必需的选项,除非您在命令行中包含“--debug”。 无论您是否使用“--debug”,您都可以覆盖任何其他选项。

当然,如果这对您很重要,您可以进行更明确的逻辑检查。 我将“--debug”作为示例,说明如果您只是要覆盖“input_file”和“output_file”变量,则如何省略“mount_point”等基本选项。

这里的主要思想是,通过将选项名称集保留在数组中,您可以使用相对较少的代码对组进行逻辑检查。

use Getopt::Long;

my @required_opts = qw(
    mount_point
    sub_dir
    database_name
    database_schema
);

my @internal_opts = qw(
    input_directory
    output_directory
    log_directory
    database_directory
    database_scripts
    input_file
    output_file
);

my @opt_spec = ("debug", map { "$_:s" } @required_opts, @internal_opts);

# Populate variables from the command line:
GetOptions( \(my %opts), @opt_spec );

# check required options unless 
my @errors = grep { ! exists $opts{$_} } @required_options;
if ( @errors && ! $opts{debug} ) {
    die "$0: missing required option(s): @errors\n";
}

################################################################################
# Directory variables
###############################################################################
my $opts{input_directory}    ||= "/$opts{mount_point}/$opts{sub_dir}/input";
my $opts{output_directory}   ||= "/$opts{mount_point}/$opts{sub_dir}/output";
my $opts{log_directory}      ||= "/$opts{mount_point}/$opts{sub_dir}/log";
my $opts{database_directory} ||= "/db/$opts{database_name}";
my $opts{database_scripts}   ||= "$opts{database_directory}/scripts";

################################################################################
# File variables
################################################################################
my $opts{input_file}    ||= "$opts{input_directory}/input_file.dat";
my $opts{output_file}   ||= "$opts{output_directory}/output_file.dat";
# ... etc

Here's another approach. It uses arrays of names and a hash to store the options. It makes all options truly optional, but validates the required ones unless you include "--debug" on the command line. Regardless of whether you use "--debug", you can override any of the others.

You could do more explicit logic checks if that's important to you, of course. I included "--debug" as an example of how to omit the basic options like "mount_point" if you're just going to override the "input_file" and "output_file" variables anyway.

The main idea here is that by keeping sets of option names in arrays, you can include logic checks against the groups with relatively little code.

use Getopt::Long;

my @required_opts = qw(
    mount_point
    sub_dir
    database_name
    database_schema
);

my @internal_opts = qw(
    input_directory
    output_directory
    log_directory
    database_directory
    database_scripts
    input_file
    output_file
);

my @opt_spec = ("debug", map { "$_:s" } @required_opts, @internal_opts);

# Populate variables from the command line:
GetOptions( \(my %opts), @opt_spec );

# check required options unless 
my @errors = grep { ! exists $opts{$_} } @required_options;
if ( @errors && ! $opts{debug} ) {
    die "$0: missing required option(s): @errors\n";
}

################################################################################
# Directory variables
###############################################################################
my $opts{input_directory}    ||= "/$opts{mount_point}/$opts{sub_dir}/input";
my $opts{output_directory}   ||= "/$opts{mount_point}/$opts{sub_dir}/output";
my $opts{log_directory}      ||= "/$opts{mount_point}/$opts{sub_dir}/log";
my $opts{database_directory} ||= "/db/$opts{database_name}";
my $opts{database_scripts}   ||= "$opts{database_directory}/scripts";

################################################################################
# File variables
################################################################################
my $opts{input_file}    ||= "$opts{input_directory}/input_file.dat";
my $opts{output_file}   ||= "$opts{output_directory}/output_file.dat";
# ... etc
丘比特射中我 2024-07-17 06:18:24

我想我要做的是将 input_directory 等设置为“undef”,然后将它们放入 getopts 中,然后测试它们是否仍然是 undef,如果是,则按所示分配它们。 如果您的用户在技术上足够成熟,能够理解“如果我给出一个相对路径,它是相对于 $mount_point/$sub_dir”,那么我会进行额外的解析来查找初始“/”。

I think what I would do is set input_directory et al to "undef", and then put them in the getopts, and then afterwards, test if they're still undef and if so assign them as shown. If your users are technically sophisticated enough to understand "if I give a relative path it's relative to $mount_point/$sub_dir", then I'd do additional parsing looking for an initial "/".

杯别 2024-07-17 06:18:24

可以使用数组作为输入数据来调用 GetOptions。 阅读文档

GetOptions can be called with an array as its input data. Read the documentation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文