Perl 系统调用中的 => 和逗号运算符

The => and comma operators in a Perl system call

我是 Perl 的初学者,如果我的问题很简单,请多多包涵。

我正在尝试修复一个相当大的 Perl 项目中的错误。通过一些反复试验和打印语句,我可以弄清楚以下对 imagemagick 的系统调用是造成该错误的原因:

system(
    composite => $tmpfilename,
    -gravity => $gravity_combo->get_active_text,
    $filename => $tmpfilename2
);

代码执行仅在此系统调用处停止,不发出任何输出。一次偶然的机会,当我试图从这个系统调用中获取一些输出时,我发现如果我用以下

替换系统调用
my $command = "composite " . $tmpfilename . " -gravity " . $gravity_combo->get_active_text . " " . $filename . " " . $tmpfilename2;

my $output = `$command`;

命令执行成功

如您所见,逗号和 => 箭头由字符串连接代替,system() 由反引号代替,但除此之外,命令是相同的。

现在我想知道这是怎么发生的,是否有更优雅的方式来以工作方式编写命令。

附录:

我不想分享完整的代码,因为,一方面,这是一个有点流行的应用程序的代码,我不想给人留下像我这样的业余爱好者正在研究它的印象(我我不是开发人员,只是一个团队成员,主要处理错误跟踪器上的用户支持);另一方面,我没想到在这种情况下更多的上下文会有所帮助。但由于一些用户要求更多的上下文,而且问题似乎比我最初假设的要复杂得多,我将提供完整的代码。

有问题的代码来自 Linux 桌面屏幕截图工具 Shutter 的插件。整个项目可以在 https://github.com/shutter-project/shutter, the specific plugin in question is at https://github.com/shutter-project/shutter/blob/master/share/shutter/resources/system/plugins/perl/spwatermark/spwatermark

找到

启动插件时会显示一个对话框,用户可以在其中输入一些字段。对话框如下所示:

首次启动时,它会执行函数 fct_imagemagick_watermark()(从第 254 行开始)。每当单击刷新或保存按钮时,都会发送一个重新启动此功能的信号(信号在第 164 和 167 行中发送)。在该函数内部,调用了一个子函数 apply_effect(),它从第 341 行开始,在第 354 行和第 365 行包括对 imagemagick 命令的两个系统调用。实际上,当通过刷新或保存按钮调用该函数时,两个系统调用都会失败,但是如果使用反引号和连接点编写命令,则这些命令有效。

$gravity_combo->get_active_text是对话框右上角下拉菜单的值(例如“居中”)。 227行左右生成了两个tmpfilename变量,filename变量来自主程序

代码到此为止,大概能看懂多少,有问题请再问!

附录 2:原始问题系统调用中变量的可能值:

$tmpfile = /tmp/euQgBTRpnf.png
$tmpfile2 = /tmp/eftbUp8eNf.png
$filename = /home/user/myimage.png
$gravity_combo = Gtk3::ComboBoxText=HASH(0x55e315db9568)
$gravity_combo->get_active-text = "Center"

我想你可以 space 像这样输出参数:

my $cmd =" composite   $tempfilename
          -gravity     $gravity_combo->get_active_text
          $filename   $tmpfilename2)
         ";
$cmd =~ tr/\n//d; 
$result = `$cmd`;

=> 也称为“胖逗号”,通常用于哈希结构,但您这里没有。

好的,我现在明白了。 "$gravity_combo->get_active_text" 是一个可执行函数。 如果它在引号内,它将不会执行。 这个有用吗?

my $cmd = " composite ".    $tempfilename.
          " -gravity  ".    $gravity_combo->get_active_text().
          " $filename ".    $tmpfilename2;   

好的,我一直在看评论。我回到原来的问题并实现了一个重力对象,以便调用语法有效。当我在下面调用 foo() 而不是 system() 时,它看起来“有效”并且返回的这个字符串也应该在系统调用中有效。考虑这段代码:

use strict;
use warnings;

my $tmpfilename = "temp";
my $filename = "somefile";
my $tmpfilename2 = "temp2";

package Gravity;
use strict;
use warnings;
sub new
{
    my $class = shift;
    my $me =  {};

    bless($me, $class);

    $me ->{active_text} = "ABC";
    return $me;
}

sub get_active_text
{
   my $me = shift;
   return $me->{active_text};
}

my $gravity_combo = new Gravity;

## call foo() instead of system() and see what it prints...

foo (
    composite => $tmpfilename,
    -gravity => $gravity_combo->get_active_text,
    $filename => $tmpfilename2
);

sub foo
{
    print "@_\n"; #system ("@_"); should work?
}

# prints: composite temp -gravity ABC somefile temp2

据我所知,创建一个像 foo() 这样的辅助函数,并让它生成一个字符串以传递给系统调用。看起来这可以防止陷入引用规则的泥潭。上面 foo() 的参数看起来“漂亮”(根据原始问题很好地格式化并且至少在我看来令人愉悦)。

没有足够的信息来完整回答,但我们可以解释其中的区别。它是双重的。

首先,"fat comma" 询问了

The => operator (sometimes pronounced "fat comma") is a synonym for the comma except that it causes a word on its left to be interpreted as a string if it begins with a letter or underscore and is composed only of letters, digits and underscores. This includes operands that might otherwise be interpreted as operators, constants, single number v-strings or function calls.

所以,它左边的单词被(双)引号简单粗暴地引用了——而一些原本可能被视为运算符或函数的字符或字符串现在却不是。这应该不重要,因为只有 $filename 是一个变量, 应该很清楚。

那么基于粗逗号的行就是一个简单的列表

system( 
    "composite", $tmpfilename, 
    "-gravity", $gravity_combo->get_active_text, 
    $filename, $tmpfilename2
);

$filename 无论如何都会被插值,所以我省略了不需要的引号)

第二个区别更微妙,负载也更大:当您给 system 一个列表时,它不会调用 shell 而是直接使用系统调用,其中第一个参数是命令名称和其他名称作为参数传递给该命令。

您发现有效的 运行 命令的另一种方法是不同的: qx (反引号)形成一个字符串,其中包含传递给它的任何内容,扫描它以查找 shell 元字符,如果找到任何元字符,则将其传递给 shell 以执行。 可能会有细微的变化,但主要是它的作用。那可能很不一样。

所以一个明显的猜测是那里的某些东西需要 shell,它在反引号 (qx) 方法中得到,而不是在 system 下的列表。一种测试方法是在 this

上强制使用 shell
system('bash', '-c', "composite $tmpfilename ...");

(或者,用空格连接:join(' ', 'composite', $tmpfilename, ...) 就像你一样)

甚至可能 composite 命令本身需要 shell 之外的 运行,由于某些环境细节等原因。 (因为从评论中的链接看来,其余参数似乎是纯数字或字符串和文件名。)这只是一个简单的猜测。

我们确实需要查看详细信息,但希望这对某些人有所帮助。


当您将命令组合成字符串时,即使将其传递给 system 也会以相同的方式处理。