Как я могу включить режим отладки через переключатель командной строки для моей программы Perl? - PullRequest
5 голосов
/ 22 мая 2010

Я изучаю 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";
    }
}

Насколько я могу сказать, по крайней мере, это работает ...:

  1. переключатель -debug_mode не мешает нормальному ARGV
  2. , работают следующие командные строки:
    • . / Option.pl
    • . / Option.pl -debug_mode
    • . / Дополнительный.pl -debug_mode = v
    • . / Дополнительный.pl -debug_mode = s

Однако яозадачен, когда несколько debug_modes «смешаны», такие как:

  1. . / option.pl -debug_mode = sv
  2. .*

Я не понимаю, почему вышеприведенные строки кода «магически работают».

Я вижу, что оба «DEBUG_VERBOS» и «DEBUG_SUPPRESS_ERROR_MSGS» применимы к сценарию, что хорошоэто чакак таковые.

Однако, если есть некоторые «конфликтующие» режимы отладки, я не уверен, как установить «приоритет debug_modes»?

Кроме того, я не уверен, что мой подход достаточно хорошдля Perlists, и я надеюсь, что я получаю свои ноги в правильном направлении.

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

Я знаю, что должен быть отладочный модуль из CPAN или где-то еще, но я хочу по-настоящему минимальное решение, которое не зависит ни от какого другого модуля, кроме «по умолчанию».

И я не могу иметь никакого контроля над средой, в которой будет выполняться этот скрипт ...

Ответы [ 4 ]

6 голосов
/ 22 мая 2010

Для обработки параметров командной строки, посмотрите на 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.

Не стесняйтесь спрашивать меня: "Какого черта?" Тип вопросов. Я понимаю, что много кидаю в тебя.

2 голосов
/ 22 мая 2010

Чтобы ответить на этот вопрос:

Я не понимаю, почему вышеприведенные строки кода "магически работают".

Причина в том, что вы проверяетезначения переключателя отладки с регулярным выражением, например:

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";

Тогда оба из них будут иметь значение true:

$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", то регулярное выражение также оценит как истинное.

2 голосов
/ 22 мая 2010

Исходя из вашего ответа Торе, я взломал этот образец.

#!/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, который настраивает базовый набор данных, который используется логикой основного приложения, которая находится в модуле.

Это упрощает написание модульных тестов для проверки правильности кода.

1 голос
/ 22 мая 2010

Я думаю, что вы столкнулись с двумя проблемами здесь.Во-первых, для обработки более сложного синтаксического анализа командной строки используйте переключатель ядра Getopt :: Std или Getopt :: Long вместо ключа командной строки -s.

Вторая проблема (я думаю) заключается в том, что вы пытаетесь каким-то волшебным способом пропустить ваши операторы отладки, когда включен режим отладки.Я не знаю ни одного стандартного модуля, который бы это делал, но это возможно с различными конструкциями:

eval {  ...code block... } if($debug);

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...