只是无法让 Perl 按预期工作(条件和变量声明)

发布于 2024-09-10 03:49:00 字数 3164 浏览 4 评论 0原文

编辑:

这次我会尝试更好的解释,这是我的脚本中的确切代码。

#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,第二个选项卡定义了从 GET 读取的变量 $id_client: ?id_client=36124 => $id_client = 36124; ,两者都包含视频中的库“locallib.pl”,

  1. 当使用所有 新代码注释了页面加载
  2. 当取消注释定义的行时, @allowed_locations = (); 该 脚本失败请
  3. 保留此定义并取消注释 if 块,以及定义 my $a; 在 if 块中;现在,当 $id_client 为时,脚本可以正常工作 已定义,但在 $id_client 时失败 未定义
  4. 取消注释 else 块和 else 中 my $a; 的定义 堵塞。现在脚本运行良好 无论有没有 $id_client,
  5. 现在评论所有 my $a; 定义并评论其他 块,脚本失败
  6. ,但如果我使用 open() 打开 IF 之前的文件,以及 close() 如果即使 IF 块也不会失败,则在之后关闭它 为空,如果没有则事件 else 块

我已经复制了在命令行中运行脚本时的所有步骤,并且脚本在每个步骤之后都起作用。
我知道这听起来不可能是脚本的行为,但请观看视频(2 分钟),也许您会注意到我在那里做错了一些事情。

使用perl版本:

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

有人问我是否没有测试服务器,回答:没有,我公司有一个生产服务器,有多种用途,不仅仅是Web界面,我不能冒险更新内核或perl版本,并且不能冒险安装任何调试器,正如公司所有者所说:“如果它有效,就不要管它”,对于他们来说, my ($a); 的解决方案是完美的,因为它有效,我'我在这里只是为了我自己,了解更多关于 Perl 的知识,并了解出了什么问题以及下次我可以做得更好。

编辑: 我成功启动了错误日志记录,并在导致失败的每个步骤之后的错误日志中找到了此信息,我收到了以下消息:
[Thu Jul 15 14:29:19 2010] [错误] locallib.pl 未在 /var/www/html/rdsdb4/cgi-bin/clients/quicksearch.cgi 第 2 行返回真值。
[Thu Jul 15 14:29:19 2010] [error] 脚本头过早结束:quicksearch.cgi

我发现这段代码位于 locallib.pl 中主代码的末尾,之后有子定义,并且 locallib.pl 是一个库而不是程序文件,因此 它的最后一条语句必须返回 true。 ,库末尾的简单 1; 语句确保(我将其放在子定义之后,以确保没有人在 1;) 之后的主程序中编写代码,问题得到解决。
不知道为什么在 CLI 中没有问题......

EDIT:

I will try a better explication this time, this is the exact code from my script.

#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);

First off all, somebody said that I should try to run it in command line, the script works fine in command line (no warnings, It was uncommented then), but when triyng to load in via apache in the browser it fails, please see this video where I captured the script behavior, what I tried to show in the video:

I have opened 2 tabs the first doesn't define the variable $id_client, the second defines the variable $id_client that is read from GET: ?id_client=36124 => $id_client = 36124; , both of them include the library in the video "locallib.pl"

  1. When running the script with all the
    new code commented the page loads
  2. when uncoment the line that defines
    the @allowed_locations = (); the
    script fails
  3. leave this definition and uncoment
    the if block, and the definition of
    my $a; in the if block; Now the script works fine when $id_client is
    defined, but fails when $id_client
    is not defined
  4. Uncoment the else block and the
    definition of my $a; in the else
    block. Now the script works fine
    with or without $id_client
  5. now comment all the my $a;
    definisions and comment the else
    block, the script fails
  6. but if I'm using open() to open
    a file before the IF, and
    close() to close it after the if it does't fail even if the IF block
    is empty and event if there is no
    else block

I have replicated all the steps when running the script in the command line, and the script worked after each step.
I know it sounds like something that cannot be the behavior of the script, but please watch the video (2 minutes), maybe you will notice something that I'm doing wrong there.

Using perl version:

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

Somebody asked if I don't have a test server, answer: NO, my company has a production server that has multiple purposes, not only the web interface, and I cannot risk to update the kernel or the perl version, and cannot risk instaling any debuger, as the company owners say: "If it works, leave it alone", and for them the solution with my ($a); is perfect beacause it works, I'm asking here just for me, to learn more about perl, and to understand what is going wrong and what can I do better next time.

EDIT:
I had success starting the error logging, and found this in the error log after each step that resulted in a failure I got this messages:
[Thu Jul 15 14:29:19 2010] [error] locallib.pl did not return a true value at /var/www/html/rdsdb4/cgi-bin/clients/quicksearch.cgi line 2.
[Thu Jul 15 14:29:19 2010] [error] Premature end of script headers: quicksearch.cgi

What I found is that this code is at the end of the main code in the locallib.pl after this there are sub definitions, and locallib.pl is a library not a program file, so it's last statement must returns true. , a simple 1; statement at the end of the library ensures that (I put it after sub definitions to ensure that noobody writes code in the main after the 1;) and the problem was fixed.
Don't know why in CLI it had no problem ...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

蒗幽 2024-09-17 03:49:00
  1. 您需要明确检查定义。

    如果要在定义$client时进入循环,
    使用if(已定义$client)

    如果你想在$client被定义并且是一个有效的整数时进入循环,
    使用 if (已定义 $client && $client =~ /^-?\d+$/ )
    我假设它是上下文中的整数,如果它可以是浮点数,则需要增强正则表达式 - 有一个标准 Perl 库,其中包含预装正则表达式,包括匹配浮点数的正则表达式。如果您需要非负整数,请从正则表达式的开头删除 -?

    如果您想在定义 $client 且非零时进入循环(并假设它不应该是空字符串),
    使用 if ( $client )

    如果要在定义 $client 且为有效的非零整数时进入循环,
    使用 if ( $client && $client =~ /^-?\d+$/ )

  2. if 条件为 false 时,您的 @ids 为“undef”,如果代码依赖于 @ids 为数组,则稍后可能会破坏代码。由于您实际上并未指定脚本在没有 else 的情况下如何中断,因此这是最可能的原因。

请查看此版本是否有效(使用上面您需要的任何“if”条件,我选择了最后一个,因为它似乎与原始代码的意图最接近 - 仅输入非零整数):

更新代码带调试

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";
  1. You need to explicitly check for definedness.

    If you want to enter the loop when $client is defined,
    use if ( defined $client ).

    If you want to enter the loop when $client is defined and a valid integer,
    use if ( defined $client && $client =~ /^-?\d+$/ ).
    I assume it's an integer from the context, if it can be a float, the regex needs to be enhanced - there's a standard Perl library containing pre-canned regexes, including ones to match floats. If you require a non-negative int, drop -? from regex's start.

    If you want to enter the loop when $client is defined and a non-zero (and assuming it shouldn't ever be an empty string),
    use if ( $client ).

    If you want to enter the loop when $client is defined and a valid non-zero int,
    use if ( $client && $client =~ /^-?\d+$/ ).

  2. Your @ids is "undef" when if condition is false, which may break the code later on if it relies on @ids being an array. Since you didn't actually specify how the script breaks without an else, this is the most likely cause.

Please see if this version works (use whichever "if" condition from above you need, I picked the last one as it appears to match the closest witrh the original code's intent - only enter for non-zero integers):

UPDATED CODE WITH DEBUGGING

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";
绝情姑娘 2024-09-17 03:49:00

当检查字符串时, == 和 != 应该分别是 'eq' 或 'ne'

if( $client != "" )

应该是

if( $client ne "" )

否则你不会得到你期望得到的东西。

when checking against a string, == and != should be respectively 'eq' or 'ne'

if( $client != "" )

should be

if( $client ne "" )

Otherwise you don't get what you're expecting to get.

回首观望 2024-09-17 03:49:00

始终以 开头:

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

Always begin your script with :

use warnings;
use strict;

these will give you usefull informations.

Then you could write :

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";
}
罗罗贝儿 2024-09-17 03:49:00

而不是

if( $client != "" )

尝试

if ($client)

如果你这样做,Perl 调试会更容易,

 use warnings;
 use strict;

Instead of

if( $client != "" )

try

if ($client)

Also, Perl debugging is easier if you

 use warnings;
 use strict;
久光 2024-09-17 03:49:00

我发现这段代码位于locallib.pl中主代码的末尾,之后有子定义,而locallib.pl是一个库而不是程序文件,所以它的最后一条语句必须返回true< /strong>,简单的 1;库末尾的语句确保(将其放在子定义之后,以确保没有人在 1 之后的主程序中编写代码;)并且问题得到解决。

结论:
我了解到,每次编写或修改库时,请确保最后一条语句返回 true;

What I found is that this code is at the end of the main code in the locallib.pl after this there are sub definitions, and locallib.pl is a library not a program file, so it's last statement must returns true, a simple 1; statement at the end of the library ensures that (put it after sub definitions to ensure that noobody writes code in the main after the 1;) and the problem was fixed.

The conclusion:
I have learned that every time you write a library or modify one, ensure that it's last statment returns true;

想挽留 2024-09-17 03:49:00

哦天哪...试试这个作为例子...

# 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 { Defined $client; } )",但这几乎肯定不是您正在寻找的东西!确保 $client 在程序早期有一些定义要容易得多(例如 $client = "";)。另请注意 Kaklon 关于 ne!= 之间差异的回答
  2. if (X) { stuff } else { } 不是有效的 perl。你可以这样做:if (X) { stuff } else { 1;但这有点回避问题,因为真正的问题是变量的测试,而不是 else 子句。
  3. 抱歉,对此没有任何线索 - 我认为问题出在其他地方。

我也赞同 Kinopiko 的建议,建议您在程序开头添加“use strict;”。这意味着任何 $variable @that %you 使用都必须预先定义为 "my $varable; my @that; my %you;" 这可能看起来需要更多工作,但实际上却更少而不是尝试处理代码中未定义的变量与已定义的变量。这是一个值得养成的好习惯。

请注意,my变量仅存在于定义它们的squiggliez中(整个文件周围有隐式的squiggliez:

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.

Oh my... Try this as an example instead...

# 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;
}

To your specific questions:

  1. If you want to test if $client is defined, you want "if ( eval { defined $client; } )", but that's almost certainly NOT what you're looking for! It's far easier to ensure $client has some definition early in the program (e.g. $client = "";). Also note Kaklon's answer about the difference between ne and !=
  2. if (X) { stuff } else { } is not valid perl. You could do: if (X) { stuff } else { 1; } but that's kind of begging the question, because the real issue is the test of the variable, not an else clause.
  3. Sorry, no clue on that - I think the problem's elsewhere.

I also echo Kinopiko in recommending you add "use strict;" at the start of your program. That means that any $variable @that %you use has to be pre-defined as "my $varable; my @that; my %you;" It may seem like more work, but it's less work than trying to deal with undefined versus defined variables in code. It's a good habit to get into.

Note that my variables only live within the squiggliez in which they are defined (there's implicit squiggliez around the whole file:

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.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文