使用具有多个管道进程的Perl system()检测错误

时间:2019-01-07 18:40:13

标签: perl error-handling pipe ipc

我正在尝试从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()从所有管道连接的进程中获取状态,或者如果任何一个这样的进程失败,以某种方式检测到错误?< / p>

顺便说一句,我自己测试了gpg(没有将其输出通过管道传递到mysql中),当输入了错误的$ passphrase时,它的确返回了错误代码。

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

2 个答案:

答案 0 :(得分:3)

如果您需要类似的精细控制,请不要使用外壳程序。

可以使用DBIDBD::mysql库替换对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;

答案 1 :(得分: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