无法使用 perl 中的系统命令获取已编译 C 程序执行的标准错误

Posted

技术标签:

【中文标题】无法使用 perl 中的系统命令获取已编译 C 程序执行的标准错误【英文标题】:Cannot get stderr of the execution of a compiled C program with the system command in perl 【发布时间】:2021-05-08 03:31:12 【问题描述】:

我写了一个对我非常有用的漂亮的小 perl 脚本。它允许我编译和执行 C 指令,就好像它是解释语言的指令一样。 它是我用来学习 C 语言的各种 C 编程 IDE。

这是我的使用方法:

crepl -Istdio 'char * str="text"; printf("%s\n", str);'
OUTPUT
text

crepl -Istdio,string 'char * str="text"; int i; for(i=0; i < strlen(str);i++)printf("%c", str[i]); printf("\n");'
OUTPUT
text

crepl -Istdio 'char * str="text"; int i=0; while(str[i] != '\''\0'\'')printf("%c", str[i]); i++; printf("\n");'
OUTPUT
text

这是脚本:

#!/usr/bin/perl

# this script requires this line in /etc/fstab :
#tmpfs   /tmp/ram/   tmpfs    defaults,noatime,nosuid,nodev,mode=1777,size=32M 0 0

use strict;
use warnings;
use autodie;

my @include;
$,="\n";
$\="\n";

if (not @ARGV)  exit 
elsif ($ARGV[0] =~ m/^-I/) 
    $ARGV[0] =~ s/^-I//;
    @include = split /,/, $ARGV[0];
    shift;


#my $source_code   = "/tmp/ram/c_program.c";    # requires adding a line in /etc/fstab
#my $compiled_code = "/tmp/ram/c_program.o";    # requires adding a line in /etc/fstab
my $source_code   = "./.c_program.c";
my $compiled_code = "./.c_program.o";

open my $FH, ">", $source_code;
foreach (@include)
    print $FH "#include <$_.h>";

print $FH "int main(int argc, char *argv[]) ";
print $FH $ARGV[0];
print $FH "return 0;";
print $FH "";
close $FH;

system "gcc", $source_code, "-o", $compiled_code;
system $compiled_code;

我遇到的问题是我没有收到运行时发生的错误消息。 (虽然我确实收到了编译错误消息。)

如果我这样做:

crepl -Istdio 'char * str="text"; char * copy; int i=0; while(str[i] != '\''\0'\'')copy[i]=str[i]; i++; printf("%s\n", copy);'

我没有输出。但我真的应该得到这个:

分段错误(核心转储)

因为我忘记了 malloc。复制字符串的正确方法是这样:

crepl -Istdio,string,stdlib 'char * str="text"; char * copy=malloc(strlen(str)); int i=0; while(str[i] != '\''\0'\'')copy[i]=str[i]; i++; printf("%s\n", copy);'

这很烦人。如果我没有收到 Segmentation Fault 之类的消息,我学习 C 就会有困难。

我已经尝试替换

系统 $compiled_code;

通过

系统 "$compiled_code 2>&1";

打印`$compiled_code 2>&1`;

但两者都不起作用。

你知道我可以如何修改我的脚本来获取这些错误消息吗?

谢谢。

【问题讨论】:

如果不使用 autodie 会怎样?另见How to get Capture::Tiny to print stderr and stdout upon failure? 如果你的意思是它解决了问题,那么不,它没有。但是你的意思是什么是有用的,如果文件句柄出现错误,它用于退出程序并显示错误消息 很抱歉,即使这个模块有效,我也可能不会使用它,因为它会减慢脚本速度,因为必须加载模块。这很烦人。我想让我的脚本保持适度的速度 我刚刚看到我的问题有点重复,而且 prog-fh 的解决方案也在那里 ***.com/questions/18332846/… "...我可能不会使用它,因为它会稍微减慢脚本速度..." - 这是误导。虽然任何库都会让事情变慢一点,但我看不出你会如何注意到那些 【参考方案1】:

您有时在终端中看到的Segmentation fault (core dumped) 消息不是由您启动的进程产生的,而是由启动该进程的shell 产生的。

当它启动一个进程时,shell 会通过类似于man 3 waitpid 的系统调用来等待它。 这样的系统调用告诉进程是成功退出(使用return_exit())还是被信号杀死。 在最后一种情况下,shell 会显示一条特定于导致提前终止的信号 (man 3 strsignal) 的消息。

在您的特定情况下,这不是启动您用 C 编写的进程的 shell,而是 perl 解释器。 您的进程被杀死不会使 perl 也被杀死,因此您的 shell 不会显示这样的消息。

我不会编写 perl,但我确信您可以将 system $compiled_code; 替换为与 fork()/exec()/waitpid()/strsignal() 等效的东西。

使用this page的结尾, 我认为您可以在脚本末尾尝试此操作。

system $compiled_code;
my $status = $?;
if ($status == -1) 
    print "failed to execute: $!\n";

elsif ($status & 127) 
    printf "child died with signal %d, %s coredump\n",
        ($status & 127), ($status & 128) ? 'with' : 'without';

else 
    printf "child exited with value %d\n", $status >> 8;

【讨论】:

谢谢 我不知道分段错误错误是由 shell 发送的。我像你一样复制粘贴了系统函数文档中的代码,我猜它解决了我的问题。我只记得 11 代表分段错误。我只是删除了代码的 else 部分,因为它只是噪音 @WhiteMist 另请注意,Perl 中有很好的库可用于运行和管理外部程序,正如帖子所说,它们可以完全“替换system ...”。从简单到强大,例如IPC::System::Simple、Capture::Tiny、IPC::Run3、IPC::Run 是的,抱歉,我仍然需要了解 *** 的工作原理。这是一个好主意,你建议用 fork、exec、waitpid 和 strsignal 替换 system。我已经有执行 fork exec 的 perl 脚本,我必须有最后 2 个。我不能正确知道,因为我还不知道如何使用系统调用来执行程序。这也是我一开始想学习 C 的原因之一。

以上是关于无法使用 perl 中的系统命令获取已编译 C 程序执行的标准错误的主要内容,如果未能解决你的问题,请参考以下文章

显示从 ASP.NET MVC 中的命令行进程获取的数据

Perl调用外部命令(其他脚本系统命令)的方法和区别

无法通过从两个不同子例程传递给新子例程的值来执行计算:Perl

perl 系统调用无法识别路径

ejabberd 'make' 通过“无法运行已编译的 C 程序”

XCode 4.3 - 从 XCode 4.2 升级后 perl 找不到 C 编译器