просто не могу заставить Perl работать как положено (условные выражения и объявления переменных) - PullRequest
0 голосов
/ 14 июля 2010

EDIT:

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

#use warnings;
#use Data::Dumper;
open(my $tmp_file, ">>", "/tmp/some_bad.log") or die "Can not open log file: $!\n";
#if( $id_client != "")
@allowed_locations = ();
#print $tmp_file "Before the if: ". Data::Dumper->Dump([\@allowed_locations, $id_client]) . "";
if( $id_client )
{
#    print $tmp_file "Start the if: ". Data::Dumper->Dump([\@allowed_locations, $id_client]) . "";
#    my $q = "select distinct id_location from locations inner join address using (id_db5_address) inner join zona_rural_detaliat using (id_city) where id_client=$id_client";
#    my $st =  &sql_special_transaction($sql_local_host, $sql_local_database, $sql_local_root, $sql_local_root_password, $q);
#    print $tmp_file "Before the while loop: ref(st)='". ref($st) . "\n";
#    while((my $id)=$st->fetchrow())
#    {
#       print $tmp_file "Row the while loop: ". Data::Dumper->Dump([$id])  . "";
#       my $id = 12121212;
#       push(@allowed_locations, $id);
#    }
#    print $tmp_file "After the while loop: ref(st)='". ref($st) . "\n";
#    my($a) = 1;
#} else {
#    my($a) = 0;    
}
#print $tmp_file "After the if: ". Data::Dumper->Dump([\@allowed_locations, $id_client]) . "";
close($tmp_file) or die "Can not close file: $!\n";
#&html_error(@allowed_locations);

Прежде всего, кто-то сказал, что я должен попытаться запустить его в командной строке, скрипт отлично работает в командной строке (без предупреждений , тогда он не был прокомментирован), , но когда загрузить через apache в браузер не удается , см. это видео , где я зафиксировал поведение скрипта, что я пытался показать в видео:

Я открыл 2 вкладки, первая не определяет переменную $ id_client, вторая определяет переменную $ id_client, которая читается из GET:? Id_client = 36124 => $ id_client = 36124; , оба они включают библиотеку в видео " locallib.pl "

  1. При запуске скрипта со всеми Новый код прокомментировал страницу загружает
  2. когда uncoment строка, которая определяет @ allow_locations = (); Сбой сценария
  3. оставьте это определение и оставьте комментарий блок if и определение my $ a; в блоке if; Теперь скрипт работает нормально, когда $ id_client определяется, но завершается неудачно, когда $ id_client не определено
  4. Раскомментируйте блок else и определение my $ a; в остальном блок. Теперь скрипт работает нормально с или без $ id_client
  5. теперь комментируйте все my $ a; определения и прокомментируйте остальное блок, скрипт завершается ошибкой
  6. но если я использую open () , чтобы открыть файл перед IF, и close () , чтобы закрыть его после, если он не потерпит неудачу, даже если блок IF пусто и событие, если нет еще блок

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

Использование версии perl:

[root@db]# perl -v
This is perl, v5.8.6 built for i386-linux-thread-mult

Кто-то спросил, нет ли у меня тестового сервера, ответьте: НЕТ, у моей компании есть рабочий сервер, который имеет несколько целей, не только веб-интерфейс, и я не могу рисковать обновлением ядра или версии perl, и не могу рисковать установкой какого-либо отладчика, поскольку владельцы компании говорят: «Если он работает, оставьте его в покое», и для них решение с my ($ a); идеально, потому что оно работает, я спрашиваю здесь только для меня, чтобы узнать больше о Perl и понять, что происходит не так и что я могу сделать лучше в следующий раз.

Спасибо.

P.S. надеюсь, что этот новый подход восстановит некоторые из моих -1:)

EDIT: Я успешно запустил ведение журнала ошибок и обнаружил это в журнале ошибок после каждого шага, который приводил к ошибке. Я получил следующие сообщения:
[Чт 15 июля 14:29:19 2010] [error] locallib.pl не вернул истинное значение в /var/www/html/rdsdb4/cgi-bin/clients/quicksearch.cgi строка 2.
[Чт 15 июля 14:29:19 2010] [ошибка] Преждевременное завершение заголовков скриптов: quicksearch.cgi

Я обнаружил, что этот код находится в конце основного кода в locallib.pl, после этого есть подопределения, и locallib.pl является библиотекой, а не программным файлом, поэтому это последний оператор должен возвращает true. , простой оператор 1; в конце библиотеки гарантирует, что (я поставил его после подопределений, чтобы гарантировать, что noobody пишет код в main после 1;) и проблема было исправлено.
Не знаю, почему в CLI с этим не было проблем ...

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

Спасибо всем за помощь.

Ответы [ 6 ]

4 голосов
/ 14 июля 2010
  1. Вам необходимо явно проверить на определенность.

    Если вы хотите ввести цикл, когда определено $ client, используйте if ( defined $client ).

    Если вы хотите ввести цикл, когда определено $ client и допустимое целое число, используйте if ( defined $client && $client =~ /^-?\d+$/ ).Я предполагаю, что это целое число из контекста, если это может быть число с плавающей точкой, необходимо улучшить регулярное выражение - есть стандартная библиотека Perl, содержащая предварительно консервированные регулярные выражения, в том числе для соответствия с плавающей точкой.Если вам требуется неотрицательное значение int, удалите -? с начала регулярного выражения.

    Если вы хотите войти в цикл, когда определено значение $ client и ненулевое значение (и предполагается, что оно никогда не должно быть(пустая строка), используйте if ( $client ).

    Если вы хотите войти в цикл, когда определено $ client и допустимое ненулевое целое число, используйте if ( $client && $client =~ /^-?\d+$/ ).

  2. Ваш @ids равен "undef", когда if условие ложно, что может привести к нарушению кода позже, если он полагается на @ids, являющийся массивом.Поскольку вы на самом деле не указали как сценарий ломается без else, это наиболее вероятная причина.

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

ОБНОВЛЕННЫЙ КОД С ОТЛАДКОЙ

use Data::Dumper;
open(my $tmp_file, ">", "/tmp/some_bad.log") or die "Can not open log file: $!\n";
@ids = (); # Do this first so @ids is always an array, even for non-client!
print $tmp_file "Before the if: ". Data::Dumper->Dump([\@ids, $client]) . "\n";
if ( $client && $client =~ /^-?\d+$/ ) # First expression catches undef and zero
{
    print $tmp_file "Start the if: ". Data::Dumper->Dump([\@ids, $client]) . "\n";
    my $st = &sql_query("select id from table where client=$client");
    print $tmp_file "Before the while loop: ref(st)='". ref($st) . "'\n";
    while(my $row = $st->fetchrow())
    {
       print $tmp_file "Row the while loop: ". Data::Dumper->Dump([row])  . "'\n";
       push(@ids, $row->[0]);
    }
    print $tmp_file "After the while loop: ref(st)='". ref($st) . "'\n";
    # No need to undef since both variables are lexically in this block only
}
print $tmp_file "After the if\n";
close($tmp_file) or die "Can not close file: $!\n";
3 голосов
/ 14 июля 2010

при проверке по строке == и! = Должны быть соответственно 'eq' или 'ne'

if( $client != "" )

должно быть

if( $client ne "" )

В противном случае вы не получите то, что ожидаете получить.

2 голосов
/ 14 июля 2010

Всегда начинайте свой сценарий с:

use warnings;
use strict;

. Это даст вам полезную информацию.

Тогда вы можете написать:

my @ids;

if (defined $client) {
    @ids = (); # not necessary if you run this part only once
    my $st = sql_query("select id from table where client=$client");
    while( my ($id) = $st->fetchrow ) {
       push @ids, $id;
    }
} else {
    warn '$client not defined';
}

if (@ids) {  # Your query returned something
    # do stuff with @ids
} else {
    warn "client '$client' does not exist in database";
}
2 голосов
/ 14 июля 2010

Примечание: этот ответ был удален, потому что я считаю, что это не настоящий вопрос. Я удаляю это, чтобы спасти других людей, повторяющих это.

Вместо

if( $client != "" )

1008 * попробовать *

if ($client)

Кроме того, отладка Perl проще, если вы

 use warnings;
 use strict;
0 голосов
/ 15 июля 2010

Я обнаружил, что этот код находится в конце основного кода в locallib.pl, после этого есть подопределения, и locallib.pl - это библиотека, а не программный файл, поэтому это последний оператор должен возвращает true , простой 1; оператор в конце библиотеки гарантирует, что (поставьте его после подопределений, чтобы noobody записывал код в main после 1;), и проблема была исправлена.

Заключение:
Я узнал, что каждый раз, когда вы пишете или изменяете библиотеку, убедитесь, что ее последний статус возвращает true;

0 голосов
/ 14 июля 2010

О, боже ... Попробуйте вместо этого пример ...

# Move the logic into a subroutine
# Forward definition so perl knows func exists
sub getClientIds($);       

# Call subroutine to find id's - defined later.
my @ids_from_database = &getClientIds("Joe Smith");

# If sub returned an empty list () then variable will be false.
# Otherwise, print each ID we found.

if (@ids_from_database) {
    foreach my $i (@ids_from_database) {
        print "Found ID $i \n";
    }
} else {
    print "Found nothing! \n";
}

# This is the end of the "main" code - now we define the logic.

# Here's the real work    
sub getClientIds($) {
    my $client = shift @_;       # assign first parameter to var $client
    my @ids    = ();             # what we will return

    # ensure we weren't called with &getClientIds("") or something...
    if (not $client) {
        print "I really need you to give me a parameter...\n";
        return @ids;
    }

    # I'm assuming the query is string based, so probably need to put it 
    # inside \"quotes\"
    my $st = &sql_query("select id from table where client=\"$client\"");

    # Did sql_query() fail?
    if (not $st) {
        print "Oops someone made a problem in the SQL...\n";
        return @ids;
    }

    my @result;

    # Returns a list, so putting it in a list and then pulling the first element
    # in two steps instead of one.
    while (@result = $st->fetchrow()) {
        push @ids, $result[0];
    }

    # Always a good idea to clean up once you're done.
    $st->finish();

    return @ids;
}

К вашим конкретным вопросам:

  1. Если вы хотите проверить, определен ли $ client, вам нужно "if (eval {определенный $ client;})" , но это почти наверняка НЕ то, что вы находясь в поиске! Намного проще убедиться, что $ client имеет какое-то определение в начале программы (например, $ client = ""; ). Также обратите внимание на ответ Каклона о разнице между ne и ! =
  2. if (X) {stuff} else {} не является допустимым perl. Вы можете сделать: if (X) {stuff} else {1; } но это своего рода вопрос напрашивается, потому что реальная проблема - проверка переменной, а не предложение else.
  3. Извините, понятия не имею - думаю, проблема в другом.

Я также поддерживаю Kinopiko, рекомендовав вам добавить "use strict;" в начале вашей программы. Это означает, что любая переменная $ @that%, которую вы используете, должна быть предварительно определена как "my $ varable; my @that; my% you;" Это может показаться большим количеством работы, но это меньше работы, чем пытаясь иметь дело с неопределенными против определенных переменных в коде. Это хорошая привычка.

Обратите внимание, что мои переменные живут только в том сквиглице, в котором они определены (неявный сквигглиц вокруг всего файла:

my $x = 1;
if ($x == 1) 
{
    my $x = 2;
    print "$x \n";    # prints 2. This is NOT the same $x as was set to 1 above.
}
print "$x \n";        # prints 1, because the $x in the squiggliez is gone.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...