Pergunta

Eu tenho um script Perl que configura variáveis ??perto do topo para os diretórios e arquivos que ele vai usar. Ele também requer algumas variáveis ??para ser definido como argumentos de linha de comando. Exemplo:

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

Esta multa trabalha em meus ambientes dev, teste e produção. No entanto, eu estava tentando torná-lo mais fácil de substituir certas variáveis ??(sem entrar no depurador) para desenvolvimento e teste. (Por exemplo, se eu quero definir meu input_file = "/tmp/my_input_file.dat"). Meu pensamento era usar os getOptions função para lidar com isso, algo como isto:

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 só pode ser chamado uma vez (tanto quanto eu sei). Os primeiros 4 argumentos em meu primeiro snippit são necessários, a última 7 diretamente acima são opcionais. Eu acho que uma situação ideal seria a configuração dos padrões como no meu primeiro snippit código, e, em seguida, de alguma forma, substituir qualquer um deles que foram definidos se argumentos foram passados ??na linha de comando. Eu pensei sobre como armazenar todas as minhas opções em um hash e, em seguida, usando o hash quando a criação de cada variável com o valor padrão a menos que existe uma entrada no hash, mas que parece acrescentar um monte de lógica adicional. Existe uma maneira de chamar getOptions em dois lugares diferentes no script?

Não tenho certeza se isso faz algum sentido.

Obrigado!

Foi útil?

Solução

Aqui está uma outra abordagem. Ele usa matrizes de nomes e um hash para armazenar as opções. Faz todas as opções realmente opcional, mas valida os necessários menos que você inclua "--debug" na linha de comando. Independentemente de saber se você usar "--debug", você pode substituir qualquer um dos outros.

Você poderia fazer verificações de lógica mais explícitas, se isso é importante para você, é claro. Eu incluí "--debug" como um exemplo de como omitir as opções básicas como "mount_point" se você está indo só para substituir o "input_file" e "output_file" variáveis ??de qualquer maneira.

A idéia principal aqui é que, mantendo conjuntos de nomes de opção em matrizes, você pode incluir lógica verifica contra os grupos com relativamente pouco código.

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

Outras dicas

Parece que você precisa mudar o seu programa para arquivos de configuração uso ao invés de configuração hard-coded. Dediquei um capítulo inteiro do Mastering Perl para isso. Você não quer o código fonte mudança para testar o programa.

Existem muitos módulos Perl no CPAN que a configuração fazer arquivos de um recurso fácil de adicionar. Escolha o que funciona melhor para sua entrada de dados.

Depois de conseguir um modelo melhor configuração no lugar, você pode facilmente definir valores padrão, assumir valores de vários lugares (arquivos, de linha de comando, etc.), e facilmente testar o programa com valores diferentes.

Eu acho que o que eu faria é conjunto input_directory et al para "undef", e depois colocá-los nos getopts, e depois mais tarde, o teste se eles ainda estão undef e se assim atribuir-lhes como mostrado. Se os usuários são suficientes tecnicamente sofisticada para compreender "se eu der um caminho relativo é relativo ao $mount_point/$sub_dir", então eu faria análise adicional à procura de uma inicial "/".

getOptions pode ser chamado com um array como seus dados de entrada. Leia a documentação .

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top