sma*_*ber 9 bash io-redirection shell-script stderr variable
func() {
echo 'hello'
echo 'This is an error' >&2
}
a=$(func)
b=???
Run Code Online (Sandbox Code Playgroud)
我想将 stderr 重定向到b变量而不创建临时文件。
echo $b
# output should be: "This is an error"
Run Code Online (Sandbox Code Playgroud)
有效但使用临时文件的解决方案:
touch temp.txt
exec 3< temp.txt
a=$(func 2> temp.txt);
cat <&3
rm temp.txt
Run Code Online (Sandbox Code Playgroud)
所以问题是,如何在不需要临时文件的情况stderr下将bash函数的重定向func到变量b?
Sté*_*las 10
在Linux和与(如实现这里的文档与写的临时文件壳zsh或bash前5.1版本做),你可以这样做:
{
out=$(
chmod u+w /dev/fd/3 && # needed for bash5.0
ls /dev/null /x 2> /dev/fd/3
)
status=$?
err=$(cat<&3)
} 3<<EOF
EOF
printf '%s=<%s>\n' out "$out" err "$err" status "$status"
Run Code Online (Sandbox Code Playgroud)
(其中ls /dev/null /x是在 stdout 和 stderr 上输出某些内容的示例命令)。
使用zsh,您还可以执行以下操作:
(){ out=$(ls /dev/null /x 2> $1) status=$? err=$(<$1);} =(:)
Run Code Online (Sandbox Code Playgroud)
(其中=(cmd)是一种使用临时文件和(){ code; } args匿名函数的进程替换形式)。
在任何情况下,您都希望使用临时文件。在大输出的情况下,任何使用管道的解决方案都容易出现死锁。您可以通过两个独立的管道和使用读输出和错误select()/poll()和在一个循环中,因为它来自于两个管道,而不会造成拘留所读取数据的一些读,但会被相当复杂和AFAIK,只有zsh已经select()支持内置并且只有yash一个原始接口pipe()(更多关于使用外壳重定向读取/写入相同的文件描述符)。
另一种方法是将其中一个流存储在临时内存中而不是临时文件中。像(zsh或bash语法):
{
IFS= read -rd '' err
IFS= read -rd '' out
IFS= read -rd '' status
} < <({ out=$(ls /dev/null /x); } 2>&1; printf '\0%s' "$out" "$?")
Run Code Online (Sandbox Code Playgroud)
(假设该命令不输出任何 NUL)
请注意,这$err将包括尾随的换行符。
其他方法可能是不同地装饰 stdout 和 stderr 并在阅读时删除装饰:
out= err= status=
while IFS= read -r line; do
case $line in
(out:*) out=$out${line#out:}$'\n';;
(err:*) err=$err${line#err:}$'\n';;
(status:*) status=${line#status:};;
esac
done < <(
{
{
ls /dev/null /x |
grep --label=out --line-buffered -H '^' >&3
echo >&3 "status:${PIPESTATUS[0]}" # $pipestatus[1] in zsh
} 2>&1 |
grep --label=err --line-buffered -H '^'
} 3>&1
)
Run Code Online (Sandbox Code Playgroud)
这假设 GNUgrep并且行足够短。如果行大于 PIPEBUF(Linux 上为 4K),则两个greps的输出行最终可能会以块的形式混杂在一起。