到目前为止我所做的是:
#!/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 的标准错误;但这完全绕过了 $(...)
,它只捕获 subshell 的标准输出。
你明白我的意思吗?
因此,您可以通过以最终填充其标准输出的方式重定向子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/