¿Cómo puedo anular la configuración modificable en mi programa de Perl?
-
21-08-2019 - |
Pregunta
Tengo un script en Perl que establece las variables en la parte superior de los directorios y archivos que se van a utilizar. También requiere unas pocas variables que se establezcan como argumentos de línea de comandos. Ejemplo:
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
Esto funciona bien en mis entornos dev, prueba y producción. Sin embargo, yo estaba tratando de hacer que sea más fácil para anular ciertas variables (sin entrar en el depurador) para desarrollo y pruebas. (Por ejemplo, si quiero poner mi archivo_entrada = "/tmp/my_input_file.dat"). Mi idea era utilizar la función getOptions para manejar esto, algo como esto:
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ólo pueden usarse una vez (por lo que yo sé). Se requieren los 4 primeros argumentos en mi primer snippit, el último 7 directamente encima son opcionales. Creo que la situación ideal sería configurar los valores por defecto como en mi primera snippit código, y entonces de alguna manera tienen prioridad sobre cualquiera de ellos que se han establecido si se pasan argumentos en la línea de comandos. Pensé en el almacenamiento de todas mis opciones en un hash y luego usar ese hash de la hora de configurar cada variable con el valor por defecto a menos que exista una entrada en el hash, pero que parece que añadir una gran cantidad de lógica adicional. ¿Hay una manera de llamar getOptions en dos lugares diferentes en el guión?
No estoy seguro si eso tiene algún sentido.
Gracias!
Solución
Aquí hay otro enfoque. Utiliza matrices de nombres y un hash para almacenar las opciones. Esto hace que todas las opciones verdaderamente opcional, pero valida los requeridos a menos que incluyen "--debug" en la línea de comandos. Independientemente de si se utiliza "--debug", puede sustituir cualquiera de los otros.
Se podría hacer controles lógicos más explícitos si eso es importante para usted, por supuesto. Incluí "--debug" como un ejemplo de cómo omitir las opciones básicas como "punto_de_montaje" si sólo vamos a anular el Output_file variables "" "archivo_entrada" y de todos modos.
La idea principal aquí es que al mantener conjuntos de nombres de opciones en las matrices, puede incluir controles lógicos contra los grupos con relativamente poco 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
Otros consejos
Parece que usted necesita cambiar su programa para utilizar los archivos de configuración en lugar de la configuración no modificable. He dedicado un capítulo entero de Mastering Perl para esto. Usted no quiere cambiar el código fuente para probar el programa.
Hay muchos módulos Perl en CPAN que hacer que los archivos de configuración de una característica fácil de añadir. Elija el que mejor se adapte a sus datos de entrada.
Una vez que obtenga un mejor modelo de configuración en su lugar, se puede configurar fácilmente los valores por defecto, tomar valores desde múltiples lugares (archivos, de línea de comandos, etc.), y fácilmente probar el programa con diferentes valores.
Creo que lo que me gustaría hacer es establecer input_directory
et al a "undef", y luego ponerlos en los getopts, y luego después, probar si siguen undef y si es así asignarlos como se muestra. Si los usuarios son técnicamente sofisticado suficiente para entender "si me dan una ruta relativa es relativa a $mount_point/$sub_dir
", entonces me gustaría hacer análisis adicionales en busca de un primer "/".
getOptions pueden ser llamados con una matriz como sus datos de entrada. Lea la documentación .