【问题标题】:The => and comma operators in a Perl system callPerl 系统调用中的 => 和逗号运算符
【发布时间】:2021-08-02 22:45:05
【问题描述】:

我是 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找到,具体的插件在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 是对话框右上角下拉菜单的值(例如,“Center”)。在第 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"

【问题讨论】:

  • 什么操作系统? composite 是否输出要在$output 中捕获的任何内容?你应该检查system的返回值。
  • system 不会捕获任何输出,这与反引号(或 qx() 相同)不同。然而,它应该在 shell 中执行相同的命令,这可能在 Perl 之外产生一些影响。大概这不是错误的根源,因为正如您所说,命令是相同的。什么是错误?
  • 改用system('composite', $tmpfilename, '-gravity', $gravity_combo->get_active_text, $filename, $tmpfilename2);
  • @choroba:Linux(更准确地说是 Arch Linux)。
  • @TLP:它是一个 GUI 应用程序,特别具有“刷新”和“保存”按钮。首次启动时,它会执行一个包含此系统调用的函数,这会成功。但是当我点击“刷新”或“保存”按钮时,它应该再次启动该功能,该操作没有执行并且GUI关闭。我用打印语句(系统之前的打印语句)跟踪它call 被执行,之后的那个不是当一个按钮被点击时;在初始启动时两者都被执行)。

标签: perl


【解决方案1】:

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

首先,"fat comma" 询问了一个明显而简单的问题

=> 运算符(有时发音为“fat comma”)是逗号的同义词,除了它会导致左侧的单词以字母或下划线开头且仅由以下内容组成时被解释为字符串字母、数字和下划线。这包括可能被解释为运算符、常量、单个数字 v 字符串或函数调用的操作数。

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

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

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

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

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

运行该命令的另一种方式是不同的:qx(反引号)形成一个传递给它的任何内容的字符串,扫描它以查找 shell 元字符,如果找到,将其传递给执行的外壳。 可能会有细微的变化,但大多数情况下它就是这样做的。这很可能是非常不同的。

所以一个明显的猜测是那里的某些东西需要外壳,它在反引号 (qx) 方法中得到,但不是在 system 下的列表中。测试它的一种方法是对此强制使用 shell

system('bash', '-c', "composite $tmpfilename ...");

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

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

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


当您将命令编写为字符串时,即使随后将其传递给system,它也会被以相同的方式处理。

【讨论】:

  • 谢谢!我为这个问题添加了额外的上下文,否则它似乎不完整。
  • @Photon 我不能更明确,因为您的应用程序很复杂,我无法测试,但我希望这对您有所帮助。编辑我的答案以添加上述评论。
  • 感谢您的想法,不幸的是,它没有成功。如果您在没有任何选项的情况下运行composite,我会得到很长的 imagemagick 选项输出。所以看起来bash -c 只将紧随其后的字符串作为参数而忽略其余部分。
  • @Photon 是的,当然;自从我上次处理这个问题以来已经太久了。需要将“rest”(从 shell 中用完的命令)作为一个字符串,所以 system('bash', '-c', "composite ...");(双引号以便插入变量等)。编辑答案...
  • 我不知道出了什么问题,它直接在启动时抱怨找不到临时文件之一。我会试着弄清楚明天发生了什么!
【解决方案2】:

我想你可以像这样隔开 args:

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;   

好的,我一直在看 cmets。我回到了最初的问题并实现了一个重力对象,以便调用语法起作用。当我在下面调用 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() 的 args 看起来“漂亮”(根据原始问题的格式很好,至少在我看来是令人愉悦的)。

【讨论】:

  • 谢谢,我试过了,但它不能正确解析$gravity_combo->get_active_text。它正确地替换了 $gravity_combo,但不访问 get_active_text 属性。
  • 编辑后的答案版本实际上是我所做的,我只是想知道是否有更优雅的方式,以及为什么它会失败原始方式(系统调用)。编辑:好的,不完全相同,我使用点进行连接,我认为,由于解析 $gravity_combo->get_active_text ,它不适用于仅一个大字符串。它实际上替换了“Gtk3::ComboBoxText=HASH(0x55e315db9568)->get_active_text”,而不是没有点的实际文本。
  • 关于最新的编辑,据我所知,它说Useless use of concatenation (.) or string in void context at ... 并且无法执行命令。
  • 不能在标量赋值中使用逗号分隔的列表。列表中的第一项将被分配,列表的其余部分将被丢弃(无效上下文)。该错误意味着您将 2 件事连接起来,然后将它们扔掉。
  • 感谢 cmets!我编写了更多代码来展示原始问题的语法如何与“帮助器”函数一起使用,我称之为 foo()。我没有删除我以前的任何错误,我留下了那些线条,疣和所有东西。我的最新代码对我来说似乎是一个解决方案。
猜你喜欢
  • 2011-08-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-13
  • 1970-01-01
  • 2019-08-05
  • 2017-03-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多