假设我有一个像下面这样的脚本:

useless.sh

echo "This Is Error" 1>&2
echo "This Is Output" 

我有另一个shell脚本:

alsoUseless.sh

./useless.sh | sed 's/Output/Useless/'

我想捕捉“这是错误”,或任何其他stderr从无用的。sh,到一个变量。 我们称它为ERROR。

注意,我使用了stdout。我想继续使用stdout,所以在这种情况下,将stderr重定向到stdout没有帮助。

所以,基本上,我想做

./useless.sh 2> $ERROR | ...

但这显然行不通。

我也知道我能做到

./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`

但这是丑陋和不必要的。

不幸的是,如果这里没有答案,这就是我要做的。

我希望还有别的办法。

有人有更好的主意吗?


当前回答

改进YellowApple的回答:

这是一个Bash函数,用于将stderr捕获到任何变量中

stderr_capture_example.sh:

#!/usr/bin/env bash

# Capture stderr from a command to a variable while maintaining stdout
# @Args:
# $1: The variable name to store the stderr output
# $2: Vararg command and arguments
# @Return:
# The Command's Returnn-Code or 2 if missing arguments
function capture_stderr {
  [ $# -lt 2 ] && return 2
  local stderr="$1"
  shift
  {
    printf -v "$stderr" '%s' "$({ "$@" 1>&3; } 2>&1)"
  } 3>&1
}

# Testing with a call to erroring ls
LANG=C capture_stderr my_stderr ls "$0" ''

printf '\nmy_stderr contains:\n%s' "$my_stderr"

测试:

bash stderr_capture_example.sh

输出:

 stderr_capture_example.sh

my_stderr contains:
ls: cannot access '': No such file or directory

此函数可用于捕获返回的对话框命令的选择。

其他回答

alsoUseless.sh

这将允许您通过sed等命令来输出无用的.sh脚本的输出,并将stderr保存在名为error的变量中。管道的结果被发送到stdout以供显示或传输到另一个命令中。

它设置了两个额外的文件描述符来管理执行此操作所需的重定向。

#!/bin/bash

exec 3>&1 4>&2 #set up extra file descriptors

error=$( { ./useless.sh | sed 's/Output/Useless/' 2>&4 1>&3; } 2>&1 )

echo "The message is \"${error}.\""

exec 3>&- 4>&- # release the extra file descriptors

这是一个有趣的问题,我希望有一个优雅的解决方案。遗憾的是,我最终得到了一个类似于Leffler先生的解决方案,但我要补充的是,你可以在Bash函数中调用useless来提高可读性:

#!/bin/bash

function useless {
    /tmp/useless.sh | sed 's/Output/Useless/'
}

ERROR=$(useless)
echo $ERROR

所有其他类型的输出重定向必须由一个临时文件支持。

重复Tom Hale的回答,我发现可以将重定向瑜伽包装到一个函数中,以便于重用。例如:

#!/bin/sh

capture () {
    { captured=$( { { "$@" ; } 1>&3 ; } 2>&1); } 3>&1
}

# Example usage; capturing dialog's output without resorting to temp files
# was what motivated me to search for this particular SO question
capture dialog --menu "Pick one!" 0 0 0 \
        "FOO" "Foo" \
        "BAR" "Bar" \
        "BAZ" "Baz"
choice=$captured

clear; echo $choice

几乎可以肯定,可以进一步简化。还没有进行特别彻底的测试,但它似乎可以同时使用bash和ksh。


EDIT:捕获函数的另一个版本,它将捕获的STDERR输出存储到用户指定的变量中(而不是依赖于全局$ capturing),灵感来自Léa Gris的答案,同时保留了上述实现的ksh(和zsh)兼容性:

capture () {
    if [ "$#" -lt 2 ]; then
        echo "Usage: capture varname command [arg ...]"
        return 1
    fi
    typeset var captured; captured="$1"; shift
    { read $captured <<<$( { { "$@" ; } 1>&3 ; } 2>&1); } 3>&1
}

和用法:

capture choice dialog --menu "Pick one!" 0 0 0 \
        "FOO" "Foo" \
        "BAR" "Bar" \
        "BAZ" "Baz"

clear; echo $choice

为了防止你的命令出错:

execute [INVOKING-FUNCTION] [COMMAND]

execute () {
    function="${1}"
    command="${2}"
    error=$(eval "${command}" 2>&1 >"/dev/null")

    if [ ${?} -ne 0 ]; then
        echo "${function}: ${error}"
        exit 1
    fi
}

精益生产的启示:

故意让错误变得不可能 让步骤最小 逐个完成项目 让每个人都清楚

这个问题有很多重复的地方,其中许多都有一个稍微简单的使用场景,您不希望同时捕获stderr、stdout和退出代码。

if result=$(useless.sh 2>&1); then
    stdout=$result
else
    rc=$?
    stderr=$result
fi

适用于常见的场景,在这种场景中,您希望在成功的情况下得到正确的输出,或者在失败的情况下在stderr上得到诊断消息。

注意,shell的控制语句已经检查了$?在引擎盖下;所以任何看起来像

cmd
if [ $? -eq 0 ], then ...

只是一种笨拙的,不常用的说法吗

if cmd; then ...