我正在开发可以全天运行的C ++程序。它输出到stdout,我想压缩此输出。未压缩的输出可以是许多GB。启动的Bourne Shell脚本编译C ++代码并按如下方式启动程序:
./prog | gzip > output.gz
当我使用CTRL-C中断脚本时,.gz文件总是损坏。当我从终端启动程序并使用CTRL-C中断它时,.gz文件也总是损坏。当我在终端上启动该程序并使用Linux killall终止该程序时,.gz文件就可以了。
另一方面,cat <large_file> | gzip > cat.gz
可以使用CTRL-C中断终端,并且cat.gz总是可以的。所以我怀疑cat有某种信号处理程序,我也必须在我的C ++程序中实现...但是在网上看到cat实现时,我发现没有类似的东西。不管怎样,我实现了这一点:
void SignalHandler(int aSignum)
{
exit(0);
}
void Signals()
{
signal(SIGINT, SignalHandler);
signal(SIGKILL, SignalHandler);
signal(SIGTERM, SignalHandler);
}
...甚至是bsh脚本中的某些内容,但没有任何帮助。CTRL-C之后,gz文件已损坏。
问题:
打开使用生成的文件zcat
给出了一些输出,但随后:
gzip: file.gz: unexpected end of file
。在Ubuntu的存档管理器中打开它只会弹出一个对话框An error occurred while extracting files.
尝试冲洗;没有发现问题的变化。
有关此问题的更多信息:末尾(EOCDR)签名
Fix archive (-F) - assume mostly intact archive
zip warning: bad archive - missing end signature
zip warning: (If downloaded, was binary mode used? If not, the
zip warning: archive may be scrambled and not recoverable)
zip warning: Can't use -F to fix (try -FF)
zip error: Zip file structure invalid (file.gz)
maot@HP-Pavilion-dv7:~/temp$ zip -FF file.gz --out file2.gz
Fix archive (-FF) - salvage what can
zip warning: Missing end (EOCDR) signature - either this archive
is not readable or the end is damaged
Is this a single-disk archive? (y/n): y
Assuming single-disk archive
Scanning for entries...
zip warning: zip file empty
maot@HP-Pavilion-dv7:~/temp$ ls -lh file2.gz
-rw------- 1 maot maot 22 feb 15 15:18 file2.gz
maot@HP-Pavilion-dv7:~/temp$
感谢@Maxim Egorushkin,但是它不起作用。CTRL-C对脚本的中断会在执行脚本prog
的信号处理程序之前终止。因此,我无法发送信号,它已经消失了……并且没有输出SignalHandler
。当prog
从命令行开始,输出SignalHandler
观察。编:
#include <iostream>
#include <unistd.h>
#include <csignal>
void SignalHandler(int aSignum)
{
std::cout << "prog: Interrupt signal " << aSignum << " received.\n";
fflush(nullptr);
exit(0);
}
int main()
{
for (int sig = 1; sig <=31; sig++)
{
std::cout << " sig " << sig;
signal(sig, SignalHandler);
}
while (true)
{
std::cout << "prog: Sleep ";
fflush(nullptr);
usleep(1e4);
}
}
脚本:
#!/bin/sh
onerror()
{
echo "onerror(): Started."
ps -jef | grep prog
killall -s SIGINT prog
exit
}
g++ -Wall prog.cpp -o prog
trap onerror 2
prog | gzip > file.gz
结果:
maot@HP-Pavilion-dv7:~/temp$ test.sh
^Conerror(): Started.
maot 16733 16721 16721 5781 0 16:17 pts/1 00:00:00 grep prog
prog: no process found
maot@HP-Pavilion-dv7:~/temp$
Maxim Egorushkin答案的实现。脚本:
#!/bin/sh
g++ -Wall prog.cpp -o prog
prog | setsid gzip > file.gz & wait
编:
#include <iostream>
#include <unistd.h>
#include <csignal>
void SignalHandler(int aSignum)
{
std::cout << "prog: Interrupt signal " << aSignum << " received.\n";
exit(0);
}
int main()
{
signal(SIGINT, SignalHandler);
while (true)
{
std::cout << "prog: Sleep ";
usleep(1e4);
}
}
当您按Ctrl + C时,Shell将发送SIGINT
到管道中的最后一个进程,即gzip
此处。gzip
终止,下次prog
写入stdout
时接收SIGPIPE
。
您需要发送SIGINT
给prog
它以刷新它stdout
并退出(前提是您像以前那样安装了信号处理程序),以便gzip
接收其所有输出然后终止。
您可以按以下方式运行管道:
prog | setsid gzip > file.gz & wait
它使用外壳作业控制功能在后台(该&
符号)启动管道。然后wait
作业终止。将On Ctrl+C
SIGINT
发送到前台进程,该前台进程是外壳wait
程序和同一终端进程组中的所有进程(不同于管道在前台且SIGINT
仅发送到管道中的最后一个进程)。prog
在那个组中。但是gzip
从开始setsid
将其放入另一个组中,以便它不接收SIGINT
而是在终止时在其stdin
关闭时prog
终止。
试图实现您的想法,请参见上文,但无济于事。你可以检查吗?
@ TradingDerivatives.eu为您添加了一个解决方案。