【问题标题】:How to store standard error in a variable如何将标准误差存储在变量中
【发布时间】:2021-12-09 21:47:17
【问题描述】:

假设我有如下脚本:

无用的.sh

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

我还有另一个 shell 脚本:

alsoUseless.sh

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

我想将 "This Is Error" 或 useless.sh 中的任何其他标准错误捕获到一个变量中。 我们称之为 ERROR。

请注意,我正在使用标准输出。我想继续使用标准输出,所以在这种情况下,将标准错误重定向到标准输出是没有帮助的。

所以,基本上,我想做

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

但这显然行不通。

我也知道我能做到

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

但这是丑陋和不必要的。

很遗憾,如果这里没有答案,我将不得不这样做。

我希望有另一种方式。

谁有更好的想法?

【问题讨论】:

  • 究竟要使用标准输出做什么?您只是想在控制台上查看它吗?还是您正在捕获/重定向它的输出?如果只是到控制台,则将 stdout 重定向到控制台,将 stderr 重定向到 stdout 以捕获它:ERROR=$(./useless.sh | sed 's/Output/Useless/' 2>&1 1>/dev/ttyX)
  • 对于想要同时捕获 stdout 和 stderr 的更普通的场景,请参见例如stackoverflow.com/questions/37115949/…
  • @psycotica0,看看这个可能有帮助stackoverflow.com/a/70108786/5372008

标签: bash shell redirect variables stderr


【解决方案1】:

我认为您想捕获 stderrstdoutexitcode,如果这是您的意图,您可以使用此代码:

## Capture error when 'some_command() is executed
some_command_with_err() {
    echo 'this is the stdout'
    echo 'this is the stderr' >&2
    exit 1
}

run_command() {
    {
        IFS=$'\n' read -r -d '' stderr;
        IFS=$'\n' read -r -d '' stdout;
        IFS=$'\n' read -r -d '' stdexit;
    } < <((printf '\0%s\0%d\0' "$(some_command_with_err)" "${?}" 1>&2) 2>&1)
    stdexit=${stdexit:-0};
}

echo 'Run command:'
if ! run_command; then
    ## Show the values
    typeset -p stdout stderr stdexit
else
    typeset -p stdout stderr stdexit
fi

此脚本捕获stderrstdout 以及exitcode

但是 Teo 它是如何工作的?

首先,我们使用printf '\0%s\0%d\0' 捕获stdoutexitcode。它们由 \0 aka 'null byte' 分隔。

之后,我们将printf 重定向到stderr,方法是:1&gt;&amp;2,然后我们使用2&gt;&amp;1 将所有重定向回stdout。因此,stdout 看起来像:

"<stderr>\0<stdout>\0<exitcode>\0"

printf 命令包含在&lt;( ... ) 中执行process substitution。进程替换允许使用文件名引用进程的输入或输出。这意味着&lt;( ... ) 将使用第一个&lt;(printf '\0%s\0%d\0' "$(some_command_with_err)" "${?}" 1&gt;&amp;2) 2&gt;&amp;1stdout 传递到command groupstdin

然后,我们可以使用read 从命令组的stdin 中捕获管道stdout。此命令从文件描述符stdin 中读取一行并将其拆分为字段。只有在 $IFS 中找到的字符被识别为单词分隔符。 $IFSInternal Field Separator 是一个变量,用于确定 Bash 在解释字符串时如何识别字段或单词边界。 $IFS 默认为空格(空格、制表符和换行符),但可以更改,例如,解析逗号分隔的数据文件。请注意,$* 使用 $IFS 中的第一个字符。

## Shows whitespace as a single space, ^I(horizontal tab), and newline, and display "$" at end-of-line.
echo "$IFS" | cat -vte
# Output:
# ^I$
# $

## Reads commands from string and assign any arguments to pos params
bash -c 'set w x y z; IFS=":-;"; echo "$*"'
# Output:
# w:x:y:z

for l in $(printf %b 'a b\nc'); do echo "$l"; done
# Output: 
# a
# b
# c

IFS=$'\n'; for l in $(printf %b 'a b\nc'); do echo "$l"; done
# Output: 
# a b
# c

这就是我们将IFS=$'\n'(换行符)定义为分隔符的原因。 我们的脚本使用read -r -d '',其中read -r 不允许反斜杠转义任何字符,-d '' 一直持续到第一个字符'' 被读取,而不是换行符。

最后,将some_command_with_err替换为你的脚本文件,你就可以随心所欲地捕获和处理stderrstdoutexitcode

【讨论】:

    【解决方案2】:

    我将使用find 命令

    find / -maxdepth 2 -iname 'tmp' -type d
    

    作为演示的非超级用户。访问/ 目录时,它应该抱怨“权限被拒绝”。

    #!/bin/bash
    
    echo "terminal:"
    { err="$(find / -maxdepth 2 -iname 'tmp' -type d 2>&1 1>&3 3>&- | tee /dev/stderr)"; } 3>&1 | tee /dev/fd/4 2>&1; out=$(cat /dev/fd/4)
    echo "stdout:" && echo "$out"
    echo "stderr:" && echo "$err"
    

    给出输出:

    terminal:
    find: ‘/root’: Permission denied
    /tmp
    /var/tmp
    find: ‘/lost+found’: Permission denied
    stdout:
    /tmp
    /var/tmp
    stderr:
    find: ‘/root’: Permission denied
    find: ‘/lost+found’: Permission denied
    

    terminal 输出也包含 /dev/stderr 内容,就像您在没有任何脚本的情况下运行 find 命令一样。 $out/dev/stdout$err/dev/stderr 内容。

    使用:

    #!/bin/bash
    
    echo "terminal:"
    { err="$(find / -maxdepth 2 -iname 'tmp' -type d 2>&1 1>&3 3>&-)"; } 3>&1 | tee /dev/fd/4; out=$(cat /dev/fd/4)
    echo "stdout:" && echo "$out"
    echo "stderr:" && echo "$err"
    

    如果您不想在终端输出中看到/dev/stderr

    terminal:
    /tmp
    /var/tmp
    stdout:
    /tmp
    /var/tmp
    stderr:
    find: ‘/root’: Permission denied
    find: ‘/lost+found’: Permission denied
    

    【讨论】:

      【解决方案3】:

      Tom Hale's answer 上进行了一些迭代,我发现可以将重定向瑜伽包装到一个函数中以便于重用。例如:

      #!/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。


      编辑:capture 函数的替代版本,它将捕获的 STDERR 输出存储到用户指定的变量中(而不是依赖于全局 $captured),灵感来自 @987654322 @ 同时保留上述实现的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
      

      【讨论】:

        【解决方案4】:

        改进YellowApple's answer:

        这是一个 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
        

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

        【讨论】:

          【解决方案5】:

          一个简单的解决方案

          { ERROR=$(./useless.sh 2>&1 1>&$out); } {out}>&1
          echo "-"
          echo $ERROR
          

          将产生:

          This Is Output
          -
          This Is Error
          

          【讨论】:

          • 我喜欢这个。我将其调整为: OUTPUT=$({ ERROR=$(~/code/sh/x.sh 2>&1 1>&$TMP_FD); } {TMP_FD}>&1) 这也允许通过以下方式查看状态$?
          【解决方案6】:

          为了读者的利益,这里有这个食谱

          • 可以重新用作 oneliner 以将 stderr 捕获到变量中
          • 仍然可以访问命令的返回码
          • 牺牲一个临时文件描述符 3(当然可以由您更改)
          • 并且不会将此临时文件描述符暴露给内部命令

          如果您想将某些command 中的stderr 捕捉到var,您可以这样做

          { var="$( { command; } 2>&1 1>&3 3>&- )"; } 3>&1;
          

          然后你就拥有了一切:

          echo "command gives $? and stderr '$var'";
          

          如果command 很简单(不像a | b),你可以离开内部的{}

          { var="$(command 2>&1 1>&3 3>&-)"; } 3>&1;
          

          包装成一个易于重复使用的 bash 函数(local -n 可能需要版本 3 及更高版本):

          : catch-stderr var cmd [args..]
          catch-stderr() { local -n v="$1"; shift && { v="$("$@" 2>&1 1>&3 3>&-)"; } 3>&1; }
          

          解释:

          • local -n 别名“$1”(这是 catch-stderr 的变量)
          • 3&gt;&amp;1 使用文件描述符 3 来保存标准输出点
          • { command; }(或“$@”)然后在捕获$(..) 的输出中执行命令
          • 请注意,此处的确切顺序很重要(以错误的方式执行此操作会错误地打乱文件描述符):
            • 2&gt;&amp;1stderr 重定向到捕获 $(..) 的输出
            • 1&gt;&amp;3 重定向 stdout 远离输出捕获 $(..) 回到保存在文件描述符 3 中的“外部”stdout。请注意,stderr 仍然指的是 FD 1 之前指向的位置:到输出捕获$(..)
            • 3&gt;&amp;- 然后关闭文件描述符 3,因为不再需要它,这样 command 就不会突然出现一些未知的打开文件描述符。请注意,外壳仍然打开了 FD 3,但command 将看不到它。
            • 后者很重要,因为像lvm 这样的一些程序会抱怨意外的文件描述符。而lvmstderr 抱怨——这正是我们要捕捉的!

          如果您进行了相应调整,您可以使用此配方捕获任何其他文件描述符。当然除了文件描述符 1(这里的重定向逻辑是错误的,但是对于文件描述符 1,你可以像往常一样使用 var=$(command))。

          请注意,这会牺牲文件描述符 3。如果您碰巧需要该文件描述符,请随意更改数字。但请注意,一些 shell(从 1980 年代开始)可能会将 99&gt;&amp;1 理解为参数 9 后跟 9&gt;&amp;1(这对于 bash 来说没问题)。

          还要注意,通过变量使这个 FD 3 可配置并不特别容易。这使事情变得非常难以理解:

          : catch-var-from-fd-by-fd variable fd-to-catch fd-to-sacrifice command [args..]
          catch-var-from-fd-by-fd()
          {
          local -n v="$1";
          local fd1="$2" fd2="$3";
          shift 3 || return;
          
          eval exec "$fd2>&1";
          v="$(eval '"$@"' "$fd1>&1" "1>&$fd2" "$fd2>&-")";
          eval exec "$fd2>&-";
          }
          

          安全说明:catch-var-from-fd-by-fd 的前 3 个参数不得来自第 3 方。始终以“静态”方式明确地给出它们。

          所以不,不,不,catch-var-from-fd-by-fd $var $fda $fdb $command,永远不要这样做!

          如果你碰巧传入了一个变量变量名,至少要这样做: local -n var="$var"; catch-var-from-fd-by-fd var 3 5 $command

          这仍然不能保护您免受所有攻击,但至少有助于检测和避免常见的脚本错误。

          注意事项:

          • catch-var-from-fd-by-fd var 2 3 cmd..catch-stderr var cmd.. 相同
          • shift || return 只是一种防止丑陋错误的方法,以防您忘记提供正确数量的参数。也许终止 shell 会是另一种方式(但这使得从命令行测试变得困难)。
          • 例程的编写方式更易于理解。可以重写该函数,使其不需要exec,但它会变得非常丑陋。
          • 这个例程也可以重写为非bash,这样就不需要local -n。但是,你不能使用局部变量,它会变得非常丑陋!
          • 还要注意evals 以安全的方式使用。通常eval 被认为是危险的。然而在这种情况下,它并不比使用"$@"(执行任意命令)更邪恶。但是,请务必使用此处显示的准确和正确的引用(否则会变得非常非常危险)。

          【讨论】:

            【解决方案7】:

            为了防错你的命令:

            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
            }
            

            启发精益制造:

            【讨论】:

            • 惯用的解决方案是将赋值放在if 中。让我发布一个单独的解决方案。
            【解决方案8】:

            这样捕获错误文件会更整洁:

            ERROR=$(</tmp/Error)
            

            shell 可以识别这一点,并且不必运行“cat”来获取数据。

            更大的问题很难。我不认为有一个简单的方法可以做到这一点。您必须将整个管道构建到子 shell 中,最终将其最终标准输出发送到文件,以便您可以将错误重定向到标准输出。

            ERROR=$( { ./useless.sh | sed s/Output/Useless/ > outfile; } 2>&1 )
            

            请注意,分号是必需的(在经典 shell - Bourne、Korn - 当然;可能在 Bash 中也是如此)。 '{}' 对封闭的命令进行 I/O 重定向。正如所写,它也会捕获来自sed 的错误。

            警告:未经正式测试的代码 - 使用风险自负。

            【讨论】:

            • 我曾希望会有一些我不知道的非常疯狂的把戏,但看起来就是这样。谢谢。
            • 如果你不需要标准输出,你可以将它重定向到/dev/null而不是outfile(如果你和我一样,你通过谷歌找到了这个问题,不要与OP具有相同的要求)
            • 对于没有临时文件的答案,请参阅here
            • Here 是一种无需将其重定向到文件的方法;它可以来回交换stdoutstderr。但是注意,正如here所说:在bash中,最好不要假设文件描述符3未使用“
            【解决方案9】:

            POSIX

            可以通过一些重定向魔法来捕获 STDERR:

            $ { error=$( { { ls -ld /XXXX /bin | tr o Z ; } 1>&3 ; } 2>&1); } 3>&1
            lrwxrwxrwx 1 rZZt rZZt 7 Aug 22 15:44 /bin -> usr/bin/
            
            $ echo $error
            ls: cannot access '/XXXX': No such file or directory
            

            请注意,命令的 STDOUT 管道(此处为 ls)是在最里面的 { } 内完成的。如果您正在执行一个简单的命令(例如,不是管道),您可以删除这些内括号。

            您不能在命令之外使用管道,因为管道会在 bashzsh 中创建一个子 shell,并且当前 shell 无法使用对子 shell 中变量的赋值。

            重击

            bash 中,最好不要假设文件描述符 3 未使用:

            { error=$( { { ls -ld /XXXX /bin | tr o Z ; } 1>&$tmp ; } 2>&1); } {tmp}>&1; 
            exec {tmp}>&-  # With this syntax the FD stays open
            

            请注意,这在 zsh 中不起作用。


            感谢this answer 提供总体思路。

            【讨论】:

            • 你能详细解释一下这条线吗?没看懂 1>&$tmp ; { 错误=$( { { ls -ld /XXXX /bin | tr o Z ; } 1>&$tmp ; } 2>&1); } {tmp}>&1;
            • @ThiagoConrado 我假设tmp 在这种情况下只是一个存储您知道未使用的文件描述符的变量。例如,如果tmp=31&gt;&amp;$tmp 将变为1&gt;&amp;3 并且该命令将与前面解释的相同(它将在文件描述符3 中存储stdout (1),而不是stderr (2) 将转到stdout 并存储在error 变量中,最后流式传输到文件描述符3 的内容返回到文件描述符1,即stdout,因为{tmp}&gt;&amp;1 变成了3&gt;&amp;1,如果我理解正确的话)。
            【解决方案10】:

            捕获和打印标准错误

            ERROR=$( ./useless.sh 3>&1 1>&2 2>&3 | tee /dev/fd/2 )
            

            细分

            您可以使用$() 捕获标准输出,但您想捕获标准错误。所以你交换标准输出和标准错误。使用 fd 3 作为标准交换算法中的临时存储。

            如果您想捕获并打印,请使用tee 进行复制。在这种情况下,tee 的输出将被$() 捕获,而不是转到控制台,但 stderr(of tee) 仍将转到控制台,因此我们将其用作 tee 的第二个输出特殊文件 /dev/fd/2 因为 tee 需要文件路径而不是 fd 编号。

            注意:单行中的重定向非常多,而且顺序很重要。 $() 在管道末端获取 tee 的标准输出,在我们将标准输入和标准输出替换为 ./useless.sh 之后,管道本身将 ./useless.sh 的标准输出路由到 tee 的标准输入。

            使用 ./useless.sh 的标准输出

            OP 说他仍然想使用(而不仅仅是打印)标准输出,例如 ./useless.sh | sed 's/Output/Useless/'

            没问题,在交换标准输出和标准错误之前做。我建议将它移动到一个函数或文件(also-useless.sh)中,并在上面的行中调用它来代替 ./useless.sh。

            但是,如果您想捕获 stdout 和 stderr,那么我认为您必须依靠临时文件,因为 $() 一次只能执行一个,并且它会生成一个子 shell,您无法从中返回变量。

            【讨论】:

              【解决方案11】:

              这个问题有很多重复,其中很多都有一个稍微简单的使用场景,你不想捕获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 ...
              

              【讨论】:

              • 这对我有用:my_service_status=$(service my_service status 2>&1) 谢谢!!
              【解决方案12】:

              在 zsh 中:

              { . ./useless.sh > /dev/tty } 2>&1 | read ERROR
              $ echo $ERROR
              ( your message )
              

              【讨论】:

                【解决方案13】:

                这篇文章帮助我为自己的目的提出了类似的解决方案:

                MESSAGE=`{ echo $ERROR_MESSAGE | format_logs.py --level=ERROR; } 2>&1`
                

                然后,只要我们的 MESSAGE 不是空字符串,我们就会将它传递给其他东西。这将让我们知道我们的 format_logs.py 是否因某种 python 异常而失败。

                【讨论】:

                  【解决方案14】:
                  $ b=$( ( a=$( (echo stdout;echo stderr >&2) ) ) 2>&1 )
                  $ echo "a=>$a b=>$b"
                  a=>stdout b=>stderr
                  

                  【讨论】:

                  • 这看起来是个好主意,但在 Mac OSX 10.8.5 上,它会打印 a=&gt; b=&gt;stderr
                  • 我同意@HeathBorders;这不会产生所示的输出。这里的麻烦是a是在子shell中求值和赋值的,子shell中的赋值不影响父shell。 (在 Ubuntu 14.04 LTS 和 Mac OS X 10.10.1 上测试。)
                  • 在 Windows GitBash 中也是如此。所以,它不起作用。 (GNU bash, version 4.4.12(1)-release (x86_64-pc-msys))
                  • SLE 11.4也不起作用,产生@JonathanLeffler描述的效果
                  • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
                  【解决方案15】:
                  # command receives its input from stdin.
                  # command sends its output to stdout.
                  exec 3>&1
                  stderr="$(command </dev/stdin 2>&1 1>&3)"
                  exitcode="${?}"
                  echo "STDERR: $stderr"
                  exit ${exitcode}
                  

                  【讨论】:

                  • command 在这里是一个糟糕的选择,因为实际上有一个同名的内置函数。可能会更明确地说是yourCommand 或类似的。
                  【解决方案16】:

                  我是这样做的:

                  #
                  # $1 - name of the (global) variable where the contents of stderr will be stored
                  # $2 - command to be executed
                  #
                  captureStderr()
                  {
                      local tmpFile=$(mktemp)
                  
                      $2 2> $tmpFile
                  
                      eval "$1=$(< $tmpFile)"
                  
                      rm $tmpFile
                  }
                  

                  使用示例:

                  captureStderr err "./useless.sh"
                  
                  echo -$err-
                  

                  确实使用了一个临时文件。但至少丑陋的东西被包裹在一个函数中。

                  【讨论】:

                  • @ShadowWizard 对我来说毫无疑问。在法语中,冒号前面通常有一个空格。我错误地将同样的规则应用于 english 答案。检查this后,我知道我不会再犯这个错误了。
                  • @Stephan欢呼,这也被讨论过here。 :)
                  • 有比使用eval 更安全的方法。例如,如果您的 TMPDIR 变量被设置为恶意值(或者您的目标变量名称包含这样的值),printf -v "$1" '%s' "$(&lt;tmpFile)" 不会冒运行任意代码的风险。
                  • 同样,rm -- "$tmpFile"rm $tmpFile 更健壮。
                  【解决方案17】:

                  如果您想绕过临时文件的使用,您可以使用进程替换。我还没有完全让它工作。这是我的第一次尝试:

                  $ .useless.sh 2> >( ERROR=$(<) )
                  -bash: command substitution: line 42: syntax error near unexpected token `)'
                  -bash: command substitution: line 42: `<)'
                  

                  然后我尝试了

                  $ ./useless.sh 2> >( ERROR=$( cat <() )  )
                  This Is Output
                  $ echo $ERROR   # $ERROR is empty
                  

                  然而

                  $ ./useless.sh 2> >( cat <() > asdf.txt )
                  This Is Output
                  $ cat asdf.txt
                  This Is Error
                  

                  所以进程替换通常是正确的事情......不幸的是,每当我将 STDIN 包装在 &gt;( ) 中并在 $() 中尝试将其捕获到变量时,我丢失$() 的内容。我认为这是因为$() 启动了一个子进程,该子进程不再有权访问父进程拥有的 /dev/fd 中的文件描述符。

                  进程替换使我能够处理不再存在于 STDERR 中的数据流,不幸的是我似乎无法以我想要的方式操作它。

                  【讨论】:

                  • 如果你做了./useless.sh 2&gt; &gt;( ERROR=$( cat &lt;() ); echo "$ERROR" ),那么你会看到ERROR的输出。问题是进程替换是在子shell中运行的,所以子shell中设置的值不会影响父shell。
                  【解决方案18】:

                  alsoUseless.sh

                  这将允许您通过诸如sed 之类的命令来传送useless.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
                  

                  【讨论】:

                  • 使用 'exec' 设置和关闭文件描述符是一种很好的技术。如果脚本随后立即退出,则不需要关闭。
                  • 如何在变量中同时捕获stderrstdout
                  • 优秀。这有助于我实现一个 dry_run 函数,该函数可以可靠地在回显其参数和运行它们之间进行选择,而不管正在干运行的命令是否正在通过管道传输到其他文件。
                  • @t00bs: read 不接受来自管道的输入。您可以使用其他技术来实现您想要展示的内容。
                  • 可以更简单,使用:error=$( ./useless.sh | sed 's/Output/Useless/' 2>&1 1>&3 )
                  【解决方案19】:

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

                  #!/bin/bash 功能无用{ /tmp/useless.sh | sed 's/输出/无用/' } 错误=$(无用) 回声 $ERROR

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

                  【讨论】:

                    【解决方案20】:

                    重定向stderr到stdout,stdout到/dev/null,然后用反引号或者$()捕获重定向的stderr:

                    ERROR=$(./useless.sh 2>&1 >/dev/null)
                    

                    【讨论】:

                    • 这就是我在示例中包含管道的原因。我仍然想要标准输出,我想让它做其他事情,去其他地方。
                    • 对于只将输出发送到stderr的命令,捕获它的简单方法是,例如PY_VERSION="$(python --version 2&gt;&amp;1)"
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-11-29
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多