【问题标题】:Why is Perl complaining about an unterminated string here?为什么 Perl 在这里抱怨未终止的字符串?
【发布时间】:2012-01-29 14:03:04
【问题描述】:

我有一个 Perl 脚本,它在 HPUX 11i v3 上的 Perl 5.005 下运行良好,但在我的 Ubuntu 11.04 机器上的 Perl 5.10 下会导致一个小问题。

归结为如下一行:

open (CMD, "do something | grep -E 'sometext$' |");

(它实际上是在捕获ps -ef 输出以查看进程是否正在运行,但我认为这并不重要(a))。

现在这在 HPUX 环境中运行良好,但是当我在 Ubuntu 下尝试时,我得到:

sh: Syntax error: Unterminated quoted string

通过插入大量的调试语句,我将其追踪到那个有问题的行,然后开始一个一个地删除字符,直到它停止抱怨。幸运的是,$ 是我尝试的第一个,它不再给我错误,所以我将行改为:

open (CMD, "do something | grep -E 'sometext\$' |");

并且它运行良好(无论如何在 Linux 下 - 我没有在 HPUX 上测试过,因为我今天无法访问那台机器 - 如果它确实有效,我将使用这种方法,但我仍然喜欢知道为什么会出现问题)。

所以很明显$ 在我的 Linux 环境下“吞下了”单引号,但在 HPUX 环境下却没有。

我的问题很简单,为什么?当然,在 5.005 和 5.10 之间没有任何大的变化。还是我缺少某种配置项?


(a) 但是,如果您知道一个更好的方法没有外部 CPAN 模块(即,只安装基线 Perl 5.005),我会很高兴知道。

【问题讨论】:

  • “在 5.005 和 5.10 之间肯定没有任何大的变化” 这是 11 年的变化。这两个版本之间有很多的变化。查看各种 perldelta 手册页。

标签: perl


【解决方案1】:

$' 是一个特殊变量(参见perldoc perlvar)。 5.005 之前有很多版本,因此正则表达式引擎中可能发生了一些变化,使这个变量变得不同(尽管它似乎也在 5.005 中)

至于更好的方法,您至少可以只在管道中运行“ps -ef”并在 perl 中执行“grep”。

【讨论】:

  • 实际上,(Perl 中的 grep 位)可能更可取,因为它调用的外部进程更少,而且 PCRE 在不同的 UNIX 中是通用的。我假设 Perl 中的 RE 是 PC :-)
【解决方案2】:

使用下面的!!!

use strict;
use warnings;

你会得到

Use of uninitialized value $' in concatenation (.) or string

【讨论】:

    【解决方案3】:

    符号后跟任何标点符号(在标准键盘上)是 Perl 中的一个变量,无论它是否为 defined。因此,在双引号字符串中,[$@][symbol] 将始终被读取为一个标记并进行插值,除非符号被转义。

    我感觉您看到的差异与不同的系统外壳有关,而不是与 perl 的不同版本有关。

    考虑你的路线:

    open (CMD, "do something | grep -E 'sometext$' |");
    

    当 perl 看到时,它会将空的$' 变量插入到双引号字符串中,因此字符串变为:

    open (CMD, "do something | grep -E 'sometext |");
    

    此时,您的 shell 将处理如下所示的一行:

    do something | grep -E 'sometext
    

    如果成功或失败将取决于 shell 关于未终止字符串的规则(一些 shell 会大声抱怨,其他 shell 会在 eof 时自动终止字符串)。

    如果您在脚本中使用warnings pragma,您可能会收到关于插入未定义变量的警告。


    读取ps 输出的更短更简洁的方法是:

    my @lines = grep /sometext\$/, `ps -ef`;
    

    或使用显式打开:

    my @lines = grep /sometext\$/, do {
       open my $fh, '|-', 'ps -ef' or die $!;
       <$fh>
    };
    

    【讨论】:

    • 其实这个可能性很大。 HPUX 使用的是 ksh,而我的 Ubuntu 使用的是 bash。
    • @paxdiablo :严格来说,默认的 HP-UX shell 是一个类似 ksh 的 Posix shell,作为动态链接的可执行文件 - /usr/bin/sh 和静态链接的 - /sbin/嘘。 HP 以 /usr/bin/ksh 的形式提供 Korn88 ksh shell,以 /usr/dt/bin/dtksh 的形式提供 Korn93 shell。
    【解决方案4】:

    因为$' 在最近的 Perl 版本中是一个特殊变量。

    来自官方文档(perlvar):

    $': 最后一个成功匹配的字符串后面的字符串 模式匹配(不包括隐藏在 BLOCK 或 eval() 被当前 BLOCK 包围)。

    如果没有成功的模式匹配,$' 为空,您的语句基本上插入到

    open (CMD, "do something | grep -E 'sometext |");
    

    转义美元符号(适用于 Linux 的解决方案)也适用于 HPUX。

    我不确定这个变量是什么时候添加的,但我可以确认它存在于 Perl 5.8.5 中。 What's New for Perl 5.005 提到 $'(不是新功能),所以我认为它在那之前就已经存在了。

    【讨论】:

    • 是的,但是$' 在新旧版本的 Perl(甚至可能是 Perl 4.x)中是一个特殊的变量;稍微令人费解的是从 5.005 到 5.10 的变化。 OTOH,5.005 非常古老。
    【解决方案5】:

    您可能应该在字符串周围使用单引号而不是双引号,因为字符串中没有任何内容需要插值:

    open (CMD, q{do something | grep -E 'sometext$' |});
    

    最好将open 的三参数形式与词法文件句柄一起使用:

    open my $cmd, '-|', q{do something | grep -E 'sometext$'} or die 'a horrible death';
    

    我没有很好地解释为什么$' 在 5.10 中被识别为特殊变量,但在 5.005 中却不是。这是出乎意料的。

    您不能升级到 5.14.1 之类的版本是否有充分的理由?即使您不更改系统提供的 Perl,也没有明显的理由不能在其他位置安装最新版本并将其用于所有脚本编写工作。

    【讨论】:

    • 你可能是对的。看起来 HPUX 11.0 是第一个发布 Perl 的,那是 5.6。 11.3 看起来像 5.8 附带的,我可以用它作为我的基线。我正在开发的构建机器很可能是更新了操作系统但没有更新 Perl 的旧机器。安装任意版本是......公司不赞成......因为我们必须分发和安装它 - 没有让客户自己获取和安装它。尽管如此,我还是会进行调查,并且很可能会提出一个我们可以使用的更新的 Perl 版本,它在最新版本的 Solaris/AIX/HPUX/RHEL/SLES 中很常见。干杯。
    【解决方案6】:

    $' 是一个特殊变量。

    如果你想避免变量扩展,只需使用q()

    open (CMD, q(do something | grep -E 'sometext$' |));
    

    【讨论】:

      猜你喜欢
      • 2011-01-14
      • 1970-01-01
      • 1970-01-01
      • 2016-04-22
      • 1970-01-01
      • 2021-06-11
      • 1970-01-01
      • 2019-10-17
      • 1970-01-01
      相关资源
      最近更新 更多