Come posso ignorare la configurazione hard-coded nel mio programma Perl?
-
21-08-2019 - |
Domanda
Ho uno script Perl che imposta le variabili vicino alla parte superiore per le directory ei file che si userà. Richiede anche alcune variabili da impostare come argomenti della riga di comando. Esempio:
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
Questo funziona bene nei miei ambienti dev, test e produzione. Comunque, stavo cercando di rendere più facile per sovrascrivere alcune variabili (senza entrare nel debugger) per lo sviluppo e la sperimentazione. (Per esempio, se voglio impostare la mia file_input = "/tmp/my_input_file.dat"). Il mio pensiero è stato quello di utilizzare la funzione GetOptions per gestire questo, qualcosa di simile:
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 può essere chiamato solo una volta (per quanto ne so). Le prime 4 argomenti a mia prima snippit sono necessari, l'ultimo 7 direttamente sopra sono opzionali. Penso che una situazione ideale sarebbe quella di impostare i valori di default, come nel mio primo snippit codice, e quindi in qualche modo a ignorare qualcuno di loro che sono state impostate se gli argomenti sono stati passati alla riga di comando. Ho pensato a memorizzare tutte le mie opzioni in un hash e quindi utilizzando tale hash quando si imposta ogni variabile con il valore di default a meno che non esista una voce nel hash, ma che sembra aggiungere un sacco di logica aggiuntiva. C'è un modo per chiamare GetOptions in due posti diversi nello script?
Non sono sicuro se questo ha un senso.
Grazie!
Soluzione
Ecco un altro approccio. Esso utilizza array di nomi e un hash per memorizzare le opzioni. Rende tutte le opzioni veramente opzionale, ma convalida quelli richiesti a meno che non si include "--debug" sulla riga di comando. Indipendentemente dal fatto che si utilizza "--debug", è possibile ignorare tutti gli altri.
Si potrebbe fare controlli logici più espliciti, se questo è importante per voi, naturalmente. Ho incluso "--debug" come un esempio di come di omettere le opzioni di base come "mount_point", se si sta solo andando a sovrascrivere il "file_input" e variabili "output_file" comunque.
L'idea principale è che tenendo serie di nomi di opzione in array, è possibile includere controlli logici contro i gruppi con relativamente poco codice.
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
Altri suggerimenti
Sembra che tu abbia bisogno di cambiare il vostro programma di utilizzare i file di configurazione, piuttosto che la configurazione hard-coded. Ho dedicato un intero capitolo del Mastering Perl per questo. Se non si desidera modificare il codice sorgente per testare il programma.
Ci sono molti moduli Perl su CPAN che rendere i file di configurazione di una funzione semplice per aggiungere. Scegli quello che funziona meglio per i dati di input.
Una volta che si ottiene un modello migliore configurazione in atto, si può facilmente impostare i valori di default, assumere valori da luoghi multipli (file, della riga di comando, ecc), e facilmente testare il programma con valori diversi.
Credo che quello che vorrei fare è impostare input_directory
et al a "undef", e poi metterli in getopts, e poi dopo, testare se sono ancora undef e in caso affermativo assegnano loro come mostrato in figura. Se gli utenti sono tecnicamente sofisticati abbastanza per capire "se io do un percorso relativo è relativo al $mount_point/$sub_dir
", quindi farei ulteriore analisi alla ricerca di una prima "/".
GetOptions possono essere chiamati con un array come i dati di input. Leggi la documentazione .