Я изучаю Perl в "головой вперед" способ. Я - абсолютно новичок на этом языке:
Я пытаюсь иметь переключатель debug_mode от CLI, который может использоваться, чтобы управлять, как мой сценарий работает, путем включения и выключения определенных подпрограмм "".
И ниже то, что я имею до сих пор:
#!/usr/bin/perl -s -w
# purpose : make subroutine execution optional,
# which is depending on a CLI switch flag
use strict;
use warnings;
use constant DEBUG_VERBOSE => "v";
use constant DEBUG_SUPPRESS_ERROR_MSGS => "s";
use constant DEBUG_IGNORE_VALIDATION => "i";
use constant DEBUG_SETPPING_COMPUTATION => "c";
our ($debug_mode);
mainMethod();
sub mainMethod # ()
{
if(!$debug_mode)
{
print "debug_mode is OFF\n";
}
elsif($debug_mode)
{
print "debug_mode is ON\n";
}
else
{
print "OMG!\n";
exit -1;
}
checkArgv();
printErrorMsg("Error_Code_123", "Parsing Error at...");
verbose();
}
sub checkArgv #()
{
print ("Number of ARGV : ".(1 + $#ARGV)."\n");
}
sub printErrorMsg # ($error_code, $error_msg, ..)
{
if(defined($debug_mode) && !($debug_mode =~ DEBUG_SUPPRESS_ERROR_MSGS))
{
print "You can only see me if -debug_mode is NOT set".
" to DEBUG_SUPPRESS_ERROR_MSGS\n";
die("terminated prematurely...\n") and exit -1;
}
}
sub verbose # ()
{
if(defined($debug_mode) && ($debug_mode =~ DEBUG_VERBOSE))
{
print "Blah blah blah...\n";
}
}
Насколько я могу сказать, по крайней мере, это работает...:
Однако я озадачен, когда несколько debug_modes "смешаны", такие как:
Я не понимаю, почему вышеупомянутые строки кода "волшебно работают".
Я вижу оба из "DEBUG_VERBOS", и "DEBUG_SUPPRESS_ERROR_MSGS" относятся к сценарию, который прекрасен в этом случае.
Однако, если существуют некоторые "конфликтующие" режимы отладки, я не уверен, как установить "приоритет debug_modes"?
Кроме того, я не уверен, достаточно хорош ли мой подход к Perlists, и я надеюсь, что получаю ноги в правильном направлении.
Одна самая большая проблема состоит в том, что я теперь поместил если операторы в большинстве моих подпрограмм для управления их поведением под различными режимами. Это хорошо? Существует ли более изящный путь?
Я знаю, что должен быть модуль отладки от CPAN или в другом месте, но я хочу реальное минимальное решение, которое не зависит ни от какого другого модуля, чем "значение по умолчанию".
И я не могу иметь никакого контроля на среде, где этот сценарий будет выполняться...
Для обработки параметров командной строки см. Getopt :: Long . Вы получаете всевозможные удобные варианты синтаксического анализа аргументов.
Существует очень много модулей, обрабатывающих ведение журнала. Log4Perl - очень популярный модуль регистрации.
Если вы действительно хотите ограничить себя, избегая CPAN (что является плохой идеей), вы можете довольно легко собрать модуль регистрации.
Вот маленький, который я для тебя взломал. Нужны тесты, настоящая документация и так далее. Я также использовал некоторые продвинутые методы, такие как собственный метод import ()
. Есть также некоторые подводные камни, связанные с тем, что я использую одну переменную для хранения настроек DEBUG для всего приложения. Но это работает. Я использовал аналогичный модуль в одном проекте, и он мне очень понравился.
package QLOG;
use strict;
use warnings;
use Carp qw(croak);
our %DEBUG_OPTIONS;
our %VALID_DEBUG_OPTIONS;
our %DEBUG_CONFLICTS;
sub import {
my $pkg = shift;
my $target = caller();
my %opts = @_;
# Configure options
croak "Must supply an array ref of valid modes"
unless exists $opts{options};
@VALID_DEBUG_OPTIONS{ @{$opts{options}} } = ();
# Configure conflicts
if( exists $opts{conflicts} ) {
@DEBUG_CONFLICTS{ keys %{$opts{conflicts}} }
= values %{$opts{conflicts}}
}
# Export DEBUG method
{ no strict 'refs';
*{$target.'::DEBUG'} = \&DEBUG;
}
return;
}
sub DEBUG {
my $mode = shift;
croak "DEBUG mode undefined"
unless defined $mode;
return unless
( $mode eq 'ANY' and %DEBUG_OPTIONS )
or exists $DEBUG_OPTIONS{$mode};
warn "$_\n" for @_;
return 1;
}
sub set_options {
for my $opt ( @_ ) {
die "Illegal option '$opt'"
unless exists $VALID_DEBUG_OPTIONS{$opt};
$DEBUG_OPTIONS{$opt}++;
}
return;
}
sub check_option_conflicts {
for my $opt ( keys %DEBUG_OPTIONS ) {
if (exists $DEBUG_CONFLICTS{$opt}) {
for ( @{$DEBUG_CONFLICTS{$opt}} ) {
die "Debug option $opt conflicts with $_"
if exists $DEBUG_OPTIONS{$_}
}
}
}
return;
}
1;
А затем используйте это следующим образом:
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use QLOG
options => [qw(
VERBOSE
SUPPRESS_ERROR_MSGS
IGNORE_VALIDATION
SETPPING_COMPUTATION
)],
conflicts => {
VERBOSE => [qw(
SUPPRESS_ERROR_MSGS
SETPPING_COMPUTATION
)],
};
process_args();
DEBUG VERBOSE => 'Command line data parsed.';
main();
### ---------------
sub main {
DEBUG VERBOSE => 'BEGIN main()';
if( DEBUG 'ANY' ) {
print "debug_mode is ON\n";
}
else {
print "debug_mode is OFF\n";
}
warn "Error which could be surpressed\n"
unless DEBUG 'SUPPRESS_ERROR_MSGS';
}
# Get arguments and process flags like 'v' and 'sc' into strings specified
# in QLOG configuration above.
# This processing makes the nice DEBUG VERBOSE => 'blah'; syntax work.
sub process_args {
# Use Getopt::Long to parse @ARGV
my @debug_options;
GetOptions (
'debug-options=s@' => \@debug_options,
'help' => \&usage,
) or usage();
# Convert option flags to strings.
my %option_lut = qw(
v VERBOSE
s SUPPRESS_ERROR_MSGS
i IGNORE_VALIDATION
c SETPPING_COMPUTATION
);
my @options = map { # This chained map
exists $option_lut{$_} # looks in the lut for a flag
? $option_lut{$_} # translates it if found
: $_ # or passes on the original if not.
}
map { # Here we split 'cv' into ('c','v')
split //
} @debug_options;
# Really should use Try::Tiny here.
eval {
# Send a list of strings to QLOG
# QLOG will make sure they are allowed.
QLOG::set_options( @options );
QLOG::check_option_conflicts();
1; # Ensure true value returned if no exception occurs.
} or usage($@);
return;
}
sub usage {
my $message = shift || '';
$message = '' if $message eq 'help';
print <<"END";
$message
Use this proggy right.
END
exit;
}
Вы можете добавить метод, чтобы ваши отладочные сообщения подавлялись.
Что-то вроде:
sub SUPPRESSED_BY {
my $mode = shift;
return if exists $DEBUG_OPTIONS{$mode);
return @_;
}
Экспортируйте символ, а затем используйте его как:
DEBUG VERBOSE => SUPPRESSED_BY SUPPRESS_ERRORS => 'My message here';
Простота, с которой модуль регистрации может быть скомпонована вместе, привела к тому, что доступно большое количество таких модулей. Есть так много способов выполнить эту задачу и различные варианты требований при инструментировании кода, что их еще больше.Я даже написал несколько модулей регистрации для различных нужд.
Как бы то ни было, это должно дать вам серьезный шанс окунуться в воду, когда вы с головой погрузитесь в Perl.
Не стесняйтесь спросить меня: «Что за черт?» типа вопросы. Я понимаю, что много кидаю на тебя.
To ответьте на это:
Я не понимаю, почему приведенные выше строки кода «волшебным образом работают».
Причина в том, что вы проверяете значения переключателя отладки с помощью регулярного выражения, например:
if(defined($debug_mode) && !($debug_mode =~ DEBUG_SUPPRESS_ERROR_MSGS))
Итак, если у вас есть:
$debug_mode = "sv"
и в качестве напоминания:
use constant DEBUG_VERBOSE => "v";
use constant DEBUG_SUPPRESS_ERROR_MSGS => "s";
Тогда оба они будут оценка истина:
$debug_mode =~ DEBUG_SUPPRESS_ERROR_MSGS;
$debug_mode =~ DEBUG_VERBOSE;
Если вы хотите проверить ровно одно значение, вы можете попробовать:
if ($debug_mode eq DEBUG_SUPPRESS_ERROR_MSGS) {...}
if ($debug_mode eq DEBUG_VERBOSE) {...}
или еще
if ($debug_mode =~ /\bDEBUG_SUPPRESS_ERROR_MSGS\b/) {...}
if ($debug_mode =~ /\bDEBUG_VERBOSE/b\) {...}
, где \ b
указывает регулярному выражению соответствовать границе слова. Конечно, если у вас есть $ debug_mode = "s v"
, то регулярное выражение также будет оценивать значение true.
Судя по вашему ответу на Tore, я взломал этот образец.
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
my $count_letters;
my $eat_beans;
my $count_beans;
my $data_file;
GetOptions (
'count-letters' => \$count_letters,
'count-beans' => \$count_beans,
'eat-beans' => \$eat_beans,
'data-file=s' => \$data_file,
'help' => \&usage,
) or usage();
# Build code ref arrays.
my @validate_file =
( $count_beans ? \&count_beans : () ),
( $count_letters ? \&count_letters : () ),
;
my @validate_line =
( $eat_beans ? \&eat_beans : () ),
;
process_file( $data_file, \@validate_line, \@validate_file );
sub process_file {
my $file = shift;
my $validate_lines = shift;
my $validate_file = shift;
open my $fh, '<', $file or die "$file : $!";
my @data;
while( my $line = $fh->readline ) {
# Validate each line with each line validator
$_->($line) or die 'Invalid line'
for @$validate_lines;
push @data, $line;
}
# Validate the whole file with the each file validator.
$_->(\@data) or die 'Invalid file'
for @$validate_file;
}
# Put real subs here:
sub eat_beans { 1 }
sub count_beans { 1 }
sub count_letters { 1 }
Что касается тестирования, вы, вероятно, захотите поместить все свои подсистемы проверки в модуль и использовать обычные инструменты тестирования Perl (см. Test :: Simple и Test :: More, чтобы начать работу).
Мне нравится структурировать свои приложения с помощью тонкого синтаксического анализатора CLI, который настраивает базовый набор данных, который используется основной логикой приложения, живущей в модуле.
Это упрощает написание модульных тестов для проверки правильности кода.
Думаю, здесь вы столкнулись с двумя проблемами. Во-первых, для обработки более сложного синтаксического анализа командной строки используйте модули ядра Getopt :: Std или Getopt :: Long вместо ключа командной строки -s.
Вторая проблема (я думаю) заключается в том, что вы пытаетесь каким-то волшебным способом пропустить операторы отладки, когда включен режим отладки. Я не знаю ни одного стандартного модуля, который бы это делал, но это возможно с различными конструкциями:
eval { ...code block... } if($debug);
Это не значит, что изменение логики вашей программы в зависимости от того, включен ли режим отладки, обязательно является хорошей идеей. Вы должны стремиться ограничить «режим отладки», чтобы изменить вывод вашей программы, а не логику, иначе вы потратите много часов, задаваясь вопросом, почему он работает в режиме отладки, а не в «рабочем режиме».