bash:如何将一个字符串添加到 stderr 行之前,并按准确顺序组合 stdout 和 stderr 并存储在 bash 中的一个变量中?

标签 bash pipe stderr io-redirection prepend

到目前为止我所做的是:

#!/bin/bash

exec 2> >(sed 's/^/ERROR= /')

var=$(
        sleep 1 ; 
        hostname ; 
        ifconfig | wc -l ; 
        ls /sfsd; 
        ls hasdh;
        mkdir /tmp/asdasasd/asdasd/asdasd;
        ls /tmp ;
) 

echo "$var"

这确实在每个错误行的开头添加了 ERROR=,但首先显示所有错误,然后显示标准输出(不按执行顺序)。

如果我们跳过将输出存储在变量中并直接执行命令,则输出会按所需顺序出现。

如有任何专家意见,我们将不胜感激。

最佳答案

脚本的主要问题是命令替换 $(...) 仅捕获子 shell 的标准输出;子 shell 的标准错误仍然只是流向父 shell 的标准错误。碰巧的是,您已经以一种最终填充父 shell 标准输出的方式重定向了父 shell 的标准错误;但这完全绕过了 $(...),它只捕获 subshel​​l 的标准输出。

你明白我的意思吗?

因此,您可以通过以最终填充标准输出的方式重定向子shell的标准错误来解决此问题,这就是捕获的内容:

var=$(
    exec 2> >(sed 's/^/ERROR= /')
    sleep 1
    hostname
    ifconfig | wc -l
    ls /sfsd
    ls hasdh
    mkdir /tmp/asdasasd/asdasd/asdasd
    ls /tmp
)

echo "$var"

即便如此,这也不能保证行的正确排序。问题是 sed 与子 shell 中的其他所有内容并行运行,因此当它刚刚收到错误行并正忙于计划写入标准输出时,子 shell 中的后续命令之一可以继续努力并已经将更多内容写入标准输出!

您可以通过为每个命令分别启动 sed 来改进这一点,这样 shell 将等待 sed 完成,然后再继续执行下一个命令:

var=$(
    sleep 1 2> >(sed 's/^/ERROR= /')
    hostname 2> >(sed 's/^/ERROR= /')
    { ifconfig | wc -l ; } 2> >(sed 's/^/ERROR= /')
    ls /sfsd 2> >(sed 's/^/ERROR= /')
    ls hasdh 2> >(sed 's/^/ERROR= /')
    mkdir /tmp/asdasasd/asdasd/asdasd 2> >(sed 's/^/ERROR= /')
    ls /tmp 2> >(sed 's/^/ERROR= /')
)

echo "$var"

即便如此,sed 仍将与每个命令同时运行,因此如果这些命令中的任何一个是同时写入标准输出和标准错误的复杂命令,那么该命令的输出顺序被捕获的命令可能与命令实际写入它的顺序不匹配。但这可能足以满足您的目的。

您可以通过为简单命令(非管道)情况创建包装函数来稍微提高可读性:

var=$(
    function fix-stderr () {
       "$@" 2> >(sed 's/^/ERROR= /')
    }

    fix-stderr sleep 1
    fix-stderr hostname
    fix-stderr eval 'ifconfig | wc -l'   # using eval to get a simple command
    fix-stderr ls /sfsd
    fix-stderr ls hasdh
    fix-stderr mkdir /tmp/asdasasd/asdasd/asdasd
    fix-stderr ls /tmp
)

echo "$var"

关于bash:如何将一个字符串添加到 stderr 行之前,并按准确顺序组合 stdout 和 stderr 并存储在 bash 中的一个变量中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38946949/

相关文章:

regex - Bash:如何从字符串中获取数字? (可能是正则表达式)

BASH 在没有 GET 参数的情况下获取文件扩展名?

c - 使用 Fork() 和 Pipe() 将文件拆分为多个子进程

c++ - 通过linux终端将数据发送到另一个进程的stdin

r - R 中 seq() 函数的管道运算符 %>% 错误

c++ - stderr 和 stdout 的行为不同

java - 从 bash 脚本中将 Spring Boot 作为前台进程启动

bash - 如何将变量从 shell 脚本传递到 expect 脚本?

python - 如何从 Fabric 的本地命令捕获 stderr?

c++ - Delphi - 从静态链接的 MSVC++ 编译的 DLL 捕获 stdout 和 stderr 输出