Как я могу переопределить жестко запрограммированную конфигурацию в моей программе Perl?

У меня есть сценарий 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 в двух разных местах в скрипте?

Не уверен, имеет ли это смысл.

Спасибо!

10.12.2008 14:53:25
4 ОТВЕТА
РЕШЕНИЕ

Вот другой подход. Он использует массивы имен и хэш для хранения параметров. Это делает все параметры действительно необязательными, но проверяет обязательные, если вы не включите «--debug» в командной строке. Независимо от того, используете ли вы «--debug», вы можете переопределить любое из остальных.

Вы могли бы сделать более явные логические проверки, если это важно для вас, конечно. Я включил «--debug» в качестве примера того, как опустить основные параметры, такие как «mount_point», если вы все равно собираетесь переопределить переменные «input_file» и «output_file».

Основная идея здесь заключается в том, что, сохраняя наборы имен параметров в массивах, вы можете включать логические проверки для групп с относительно небольшим количеством кода.

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
6
10.12.2008 17:13:39
Мне нравится это - вы можете объяснить такие строки, как мой $ var || = "somevalue"? Как бы $ var взял опцию из командной строки? Или не хватает шага, когда он вытаскивает его из хеша% opts?
BrianH 10.12.2008 16:24:26
К сожалению. Я не закончил преобразование всех этих переменных в хэш опций. Это должно быть "$ opts {varname} || = somevalue". Оператор "|| =" равен "or-equals", который присваивает некоторое значение, если только левая переменная не равна true. В Perl 5.10 более безопасной альтернативой является "// =", которая определяет-или-равно
xdg 10.12.2008 17:20:27
Хорошо - спасибо - я подумал, что что-то пропущено, но просто хотел убедиться, что я не пропустил какую-то магическую функцию Perl :)
BrianH 10.12.2008 17:36:40
Для моего вопроса мне больше всего нравится этот ответ, потому что он выполняет то, что я пытался сделать. Это правда, что могут быть лучшие способы справиться с этим, как указал Брайан Д Фой, но этот ответ прямо отвечает на мой вопрос. Спасибо xdg!
BrianH 10.12.2008 17:39:10

GetOptions можно вызывать с массивом в качестве входных данных. Прочитайте документацию .

0
10.12.2008 15:12:59

Я думаю, что я бы сделал, установив input_directoryet al на «undef», а затем поместил их в getopts, а затем проверил, являются ли они все еще undef, и если да, назначьте их, как показано. Если ваши пользователи достаточно технически продвинуты, чтобы понять, «если я приведу относительный путь, относительно которого он $mount_point/$sub_dir», то я бы выполнил дополнительный анализ в поисках начального «/».

3
10.12.2008 15:20:37

Похоже, вам нужно изменить программу для использования файлов конфигурации, а не жестко запрограммированной конфигурации. Я посвятил этому целую главу по освоению Perl . Вы не хотите изменять исходный код для тестирования программы.

В CPAN существует множество модулей Perl, которые позволяют легко добавлять файлы конфигурации. Выберите тот, который лучше всего подходит для ваших входных данных.

Как только вы получите более совершенную модель конфигурации, вы можете легко установить значения по умолчанию, взять значения из нескольких мест (файлы, командная строка и т. Д.) И легко протестировать программу с различными значениями.

7
10.12.2008 15:23:01