【问题标题】:Detecting error using Perl system() with multiple piped processes使用带有多个管道进程的 Perl system() 检测错误
【发布时间】:2019-06-02 10:45:52
【问题描述】:

我正在尝试从 GPG 加密文件中恢复 MySQL 数据库。

以下工作非常好:

my $status = system(
    "gpg --pinentry-mode loopback --passphrase $passphrase --decrypt $my_encrypted_backup_file"
  . " | "
  . "mysql --host=myhost --user=myuser --password=mysecret mydatabase"
);

假设没有错误情况

但是,如果在第一个过程中出现错误情况(例如不正确的 $passphrase),$status == 0 会错误地指示成功。我理解这是因为状态是从第二个进程 mysql 进程返回的。

有没有一种通用的方法,使用system(),从所有管道连接的进程中获取状态,或者在任何一个这样的进程失败时以某种方式检测错误?

顺便说一句,我已经单独测试了gpg(没有将其输出通过管道传输到 mysql),当输入了不正确的 $passphrase 时,它​​确实返回了错误代码。

解决方法可能是mysql 中的某个选项标志,当它从gpg 没有收到任何内容时返回错误。另一种解决方法是分解进程并使用某种 tmp 文件。但是,我想要一个更通用的解决方案。谢谢!

【问题讨论】:

    标签: perl error-handling pipe ipc


    【解决方案1】:

    如果您需要这样的精细控制,请不要使用 shell。

    mysql 的调用可以替换为使用DBIDBD::mysql 库。 gpg 可以替换为Crypt::GPG

    如果无法做到这一点,请使用open and its |- and -| modes 自己进行管道处理。

    open(
        my $gpg_out,
        "-|",
        "gpg --pinentry-mode loopback --passphrase $passphrase --decrypt $my_encrypted_backup_file"
    ) or die "Can't run gpg: $!";
    
    open(
        my $mysql_in,
        "|-",
        "mysql --host=myhost --user=myuser --password=mysecret mydatabase"
    ) or die "Can't run mysql: $!";
    
    while(my $line = <$gpg_out>) {
        print $mysql_in $line;
    }
    
    close $gpg_out;
    close $mysql_in;
    

    【讨论】:

    • 感谢您快速详细的回答!但是,or die ... 没有捕捉到gpg 返回的错误。可能与-| 创建的子进程有关,如perldoc.perl.org/functions/open.html 中所述。只需检查$! 似乎就可以了。足够强大吗?
    • @yahermann open ... or die 将在程序最初执行时捕获错误。读取或写入时的错误必须单独捕获。 print 确实返回它是否成功,所以你可以检查一下。您可以使用eofreadline 的组合而不是&lt;&gt; 来提高阅读能力。如您所见,这很快变得复杂。我建议你使用模块或者IPC::Run
    • 进一步测试没有错误条件返回gpg: error writing to '-': Broken pipe。因此,除非我搞砸了,否则您的代码 sn-p 似乎不起作用。还尝试使用eofreadline,并检查print的返回值,仍然没有发现错误情况。唉............我会尝试IRC::Run
    • @yahermann 有些事情搞砸了。例如,perl -wle 'open my $fh, "-|", "gpg --version" or die $!; print &lt;$fh&gt;' 工作正常。我可以再次建议使用这些模块吗?
    • Re "然而,or die ... 没有捕捉到由gpg 返回的错误。",如果在close 上使用它会发生。请注意,如果您使用 open(my $gpg_out, "-|", "gpg", "--pinentry-mode", "loopback", ...),您将避免代码注入错误、获得更好的错误报告并避免创建 shell
    【解决方案2】:

    感谢@Schwern 建议 IPC::Run。这是一个有效且经过测试的解决方案:

    use IPC::Run qw( run );
    
    my $gpg = [
      "gpg",
      "--pinentry-mode=loopback",
      "--passphrase=$my_passphrase",
      "--decrypt",
      $my_backupfilepath
    ];
    
    my $mysql = [
      "mysql",
      "--host=$mysql_host"
      "--user=$mysql_user"
      "--password=$mysql_pass"
      $mysql_dbname
    ];
    
    run( $gpg, '|', $mysql ) || die "Error";
    

    我仍然无法捕获详细的错误消息,而且我仍然看到 gpg 和 mysql 的输出过于冗长....但是我已经花了足够的时间与 Perl 和 GPG 作斗争!很高兴接受改进。

    与核心问题无关,但对于按原样使用此配方的任何人...要让 GPG 2.1+ 通过命令行接受密码,并且不缓存它,您必须将以下内容添加到 gpg-agent.conf

    allow-loopback-pinentry
    default-cache-ttl 1
    max-cache-ttl 1
    

    来源:https://wiki.archlinux.org/index.php/GnuPG#pinentry

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-22
      • 2022-01-17
      • 2013-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-03
      • 2019-12-31
      相关资源
      最近更新 更多