mysql - Perl 在打开 gzip 文件时给出 "gzip: stdout: Broken pipe"错误,但前提是连接到数据库

标签 mysql perl gzip

考虑以下在 Linux 机器上运行的程序,它打开一个 gzip 压缩的输入文件:

#!/usr/bin/env perl

open (my $fileHandle, "-|", "/bin/zcat $ARGV[0]");
my $ff = <$fileHandle>;
close($fileHandle);

按预期工作(它什么都不做,但不打印错误):

$ bar.pl file.gz
$

现在,如果我使用相同的代码但之前连接到 MySQL 数据库,gzip 会报错(您可以直接运行代码,这是一个开放的数据库,凭据将起作用):

#!/usr/bin/env perl
use DBI;
use strict;
use warnings;

my $dsn = "DBI:mysql:database=hg19;host=genome-mysql.cse.ucsc.edu";
my $db =  DBI->connect($dsn, 'genomep', 'password');
my $dbResults = $db->prepare("show tables");
my $ret = $dbResults->execute();
$dbResults->finish();
$db->disconnect();

open (my $fileHandle, "-|", "/bin/zcat $ARGV[0]");
my $ff = <$fileHandle>;
close($ff);

运行上面给出:

$ foo.pl file.gz 

gzip: stdout: Broken pipe

这显然是一个复杂得多的程序的一部分,但我设法将其缩减为重现该问题的愚蠢片段。

这是怎么回事?为什么连接到数据库会影响 gzip 的行为方式?请注意,一切似乎都正常(在实际程序中,我对 gzip 数据做了一些有用的事情)但为什么我会收到该错误消息?


事实证明,此行为特定于(稍)旧版本的 Perl 和/或 DBI。在失败的机器上,我有:

  • Ubuntu
  • 为 x86_64-linux-gnu-thread-multi 构建的 Perl 5,版本 22,subversion 1 (v5.22.1)
  • DBI 1.634
  • DBD 4.033
  • gzip 1.6

然而,在另外两台机器上它确实有效。这些有:

  • Ubuntu
  • 为 x86_64-linux-gnu-thread-multi 构建的 Perl 5,版本 26,subversion 1 (v5.26.1)
  • DBI 1.640
  • DBD 4.033
  • gzip 1.6

  • Arch Linux
  • 为 x86_64-linux-thread-multi 构建的 Perl 5,版本 30,subversion 0 (v5.30.0)
  • DBI 1.642
  • DBD 4.050
  • gzip 1.10

最佳答案

至少在这里,似乎 MySQL 库(可能)正在屏蔽(忽略)SIGPIPE,这就是您所看到的。比较 strace 输出,我在 MySQL 运行中看到这样一行:

rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7f78bdf16840}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0

事实证明,您可以在不使用 MySQL 的情况下轻松复制该行为:

$SIG{PIPE} = 'IGNORE';

open (my $fileHandle, "-|", "/bin/zcat $ARGV[0]");
my $ff = <$fileHandle>;
close($ff);

或者,您可以将信号重置为默认处理程序以使消息消失,即使通过将其设置为 DEFAULT 而不是 IGNORE 连接到 MySQL 后也是如此.

顺便说一句,这是documented behavior of the MySQL library :

To avoid aborting the program when a connection terminates, MySQL blocks SIGPIPE on the first call to mysql_library_init(), mysql_init(), or mysql_connect().

(它也可能取决于 gzip 版本;也许某些 gzip 版本会在 init 上设置信号处理程序。)

最终,您看到的是,如果 gzip 收到 SIGPIPE,它就会退出。如果它从写入返回错误(因为 SIGPIPE 被忽略),它会打印一条错误消息。

关于mysql - Perl 在打开 gzip 文件时给出 "gzip: stdout: Broken pipe"错误,但前提是连接到数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57625550/

相关文章:

regex - 如何在正则表达式中将 $1 与数字连接起来

c# - C# 和 R 的 Gzip 字节数组不同

database - 在 Perl 中的数据库句柄上通过 CGI 获取 CRUD 的最快方法是什么?

compression - 解压gz SQL文件

php - 使用 PHP 编写包含 100,000 多个条目的 .tgz 文件,但避免写入单个文件

mysql - 智能搜索MySQL

php - 尝试从多维数组中提取值以在 SQL 语句中使用

mysql - 子查询不访问父查询别名

mysql - 选择列最大的数据

MySQL - Perl- Order by - 如何显示 ABS(PartNum) DESC 但是,三个不同的 (PartNum) 类别?