在csh中重定向stderr时出现问题

3

我正在编写一个Perl脚本,应该在shell中执行命令并解析它们的输出。作为shell,我打算使用csh。我已经开始了以下内容:

my $out = `cmd`

但它无法捕获标准错误输出,而我也需要。使用输出重定向运行 sh 没有任何作用。

my $out = `sh -c "cmd 2>&1"`

仍然只捕获标准输出(STDOUT),而不是标准错误(STDERR)。

即使在csh中将其重定向到文件也对我无效。

tcsh$ cmd >& logfile.log

仍然只捕获STDOUT %)

我试图执行的命令实际上是sh脚本,这个脚本中的一些命令会打印到STDERR,我想捕获这个输出。如果我执行sh -c "cmd 2>/dev/null",STDERR实际上会被发送到/dev/null,终端上只会打印STDOUT。

有人可以帮我解决这个问题吗?


我尝试运行sh shell并执行cmd 2>logfile.log,但即使在这种情况下,logfile.log也是空的。我怀疑tcsh不是主要问题。 - marvin_yorke
5个回答

2
我相信你没有告诉我们某些信息。你使用的是Cygwin还是Windows?你是否设置了PERL5SHELL环境变量?
因为在我可以轻松测试的五个平台上,这两个都能正常运行,所以我认为你没有告诉我们所有的情况。
% perl -le '$out = `sh -c "grep missing /dev/nowhere 2>&1" | cat -n`; chomp $out; print "got <<<$out>>>"'
got <<<     1   grep: /dev/nowhere: No such file or directory>>>

但是在远程操作时,没有必要显式调用sh(1)。这是因为Perl对于所有反引号、管道打开和system()的shell-out都会始终调用sh(1):

% perl -le '$out = `grep missing /dev/nowhere 2>&1 | cat -n`; chomp $out; print "got <<<$out>>>"'
got <<<     1   grep: /dev/nowhere: No such file or directory>>>

我能想到的唯一例外是在非Unix系统上发生,因为它们没有 /bin/sh,则定义了其他东西。但在任何情况下,简单的 shell-outs 都不会在背后调用 tcsh(1)。你必须严重修改 perl(1) 源代码才能实现这一点。我也相当怀疑您是否能(轻松地)修改二进制文件,因为字符串 "/bin/tcsh" 的长度将大于 "/bin/sh",而且它很少在 /bin/ 中被找到。从 shell 中无法获取 stderr 重定向工作的事实说明有些非常奇怪的事情正在发生。我认为我们需要更多信息。

你好 @tchrist,我在Linux上使用SuSE Desktop 11。我刚找到一个问题,即 STDERR 和 tcsh 在这里都不重要......你的两个例子都可以正常工作。但是,如果我把我的 cmd 放在 grep 的位置 - 它什么都没有输出。所以看来 cmd 并没有将其输出置于 STDERR 或 STDOUT 中。那么它输出到哪里了呢?可能是 /dev/tty 吗? - marvin_yorke
@marvin:我想这是可能的。你可以通过在cron(1)作业中运行它来找出。如果它正在写入/dev/tty,则仍然有可能捕获它,但这样做会很麻烦。 - tchrist
运行命令'strace cmd...',它会准确地显示它正在写入的内容(如果不是STDERR和STDOUT)。 - qneill

1

你说在tcsh中运行命令cmd >& logfile.log只会将cmd的标准输出发送到日志文件,而不是它的标准错误输出。这没有意义。

尝试用以下脚本替换cmd:

#!/bin/sh

echo stdout
echo STDERR 1>&2

日志文件logfile.log应该同时显示“stdout”和“STDERR”。

如果是这样,那么也许你的命令“cmd”做了一些奇怪的事情。我最好的猜测是cmd正在写入/dev/tty,而不是stdout或stderr;这不会受重定向影响。

为了理解我的意思,请将以下行添加到上述脚本中:

echo tty > /dev/tty

1

我实在没有时间像平常一样模拟一个例子,甚至测试一个。我想你可以尝试使用Capture::Tiny来看看是否有帮助。


1

在这里,您正在捕获sh的STDOUT,而不是cmd的STDERR:

my $out = `sh -c "cmd 2>&1"`;

你能直接运行cmd吗?
my $out = `cmd 2>&1`;

我认为 cmd 2>&1 应该将 cmd 的 STDERR 重定向到 sh 的 STDOUT,然后被 Perl 捕获。是这样吗? - marvin_yorke
你是如何让 perl 首先调用 csh 的? - reinierpost
然后cmd本身重定向了STDERR - 可能是到/dev/tty? - Ingo
@reinierpost csh 是我的工作 shell ($SHELL),所以我从中运行 Perl。 - marvin_yorke
@reinierpost,我正在尝试执行的脚本是用于NTP守护进程的rc脚本,如果有帮助的话... - marvin_yorke
显示剩余4条评论

1
  • 反引号捕获的是STDOUT而不是STDERR。
  • system将同时转储stdout和stderr到其父级设置中。
  • 如果您想要捕获 STDERR,您需要类似IPC::Open3这样的东西:

open2()非常相似,open3()生成给定的$cmd并连接CHLD_OUT以从子进程读取,CHLD_IN以写入子进程,以及CHLD_ERR以处理错误。如果CHLD_ERR为false,则


非常类似于一个经常被问到的问题,perldoc -q stderr "如何从外部命令中捕获 STDERR?" :-) - tadmc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接