【问题标题】:troubles while redirecting stderr in csh在 csh 中重定向 stderr 时出现问题
【发布时间】:2023-07-11 15:55:01
【问题描述】:

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

my $out = `cmd`

但它没有捕获我也需要的 STDERR。使用输出重定向运行 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 不是这里的主要问题

标签: perl shell redirect csh tcsh


【解决方案1】:

我相信有些事情你没有告诉我们。你在cygwin吗?还是窗户?您是否设置了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 总是 调用 sh(1) 以获取其所有反引号、管道打开和 system() shell-outs:

% 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,所以定义了其他东西。

但在任何情况下,简单的外壳都不会在你背后调用 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:我想这是可能的。您可以通过在 cron(1) 作业中运行它来找出答案。如果它正在写入 /dev/tty,仍然可以捕获它,但这样做会很痛苦。
  • 运行 'strace cmd ...' 它会准确地显示它正在写入的内容(如果它不是 STDERR 和 STDOUT)。
【解决方案2】:

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

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

你能直接运行cmd吗?

my $out = `cmd 2>&1`;

【讨论】:

  • 它不会在 csh shell 中运行 cmd 吗?如果是这样,csh 不支持2&gt;&amp;1 语法
  • cmd 2>&1 1>/dev/null 应该这样做 - 只是 STDERR,没有别的
  • 我还认为cmd 2&gt;&amp;1 应该将cmd 的STDERR 重定向到sh 的STDOUT,而不是被perl 捕获。不是这样吗?
  • @Ingo,如果我以tcsh$ sh -c "cmd 2&gt;&amp;1 1&gt;/dev/null" 运行它,它会在终端中打印 STDERR,但在 Perl 中返回空字符串:my $out = `sh -c "cmd 2&gt;&amp;1 1&gt;/dev/null"`
  • 你如何设法让perl首先调用csh
【解决方案3】:
  • 反引号捕获 STDOUT 而不是 STDERR。
  • system 会将 stdout 和 stderr 转储到其父级的设置中。
  • 如果你想捕获 STDERR,你需要像IPC::Open3这样的东西:

open2() 非常相似,open3() 产生给定的 $cmd 并连接 CHLD_OUT 以从子级读取,CHLD_IN 用于写入子级,并连接 CHLD_ERR 用于错误。如果 CHLD_ERR 为假,

【讨论】:

  • 与一个经常被问到的问题极为相似,perldoc -q stderr "How can I capture STDERR from an external command?" :-)
【解决方案4】:

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

尝试用以下脚本替换 cmd:

#!/bin/sh

echo stdout
echo STDERR 1>&2

“stdout”和“STDERR”都应该出现在 logfile.log 中。

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

要明白我的意思,请将此行添加到上面的脚本中:

echo tty > /dev/tty

【讨论】:

    【解决方案5】:

    我真的没有时间像往常那样模拟一个示例,甚至没有测试一个示例。我想您可以尝试使用Capture::Tiny 看看是否有帮助。

    【讨论】: