捕获一行代码的错误流,并使用陷阱进行报告,同时保持stdout不变



我在下面的两个shell脚本中尝试了以下(简化的(代码。

该脚本调用一个R脚本,其中运行的代码将根据发生的情况生成标准输出和错误流。

我试图实现的是像往常一样在控制台上显示输出和错误流,但当脚本运行失败时(例如,RScript产生错误流(,我想将R生成的错误消息保存在sqlite数据库中,但当它退出时,控制台上仍然显示正常的输出和错误。我已经尝试了多种形式的Rscript函数的重定向输出,但我要么最终不将任何内容保存到数据库(除了行号(,要么我可以保存错误消息,但控制台上不会显示任何内容。。。

这不会将错误消息保存到DB(行号会(,但所有内容都将在控制台上

updateDBwhenError() {
sqlite3 "myDB.db" "INSERT INTO logs VALUES('$1')"   
}
err_report() {
#Tryign to capture both line error and message
updateDBwhenError "Error Line $1 $2"
exit
}
trap 'err_report ${LINENO}' ERR
Rscript testScript.R

这将把错误消息保存到DB中,但控制台上再也没有了

updateDBwhenError() {
sqlite3 "myDB.db" "INSERT INTO logs VALUES('$1')"   
}
err_report() {
#Tryign to capture both line error and message
updateDBwhenError "Error Line $1 $rErr"
exit
}
trap 'err_report ${LINENO}' ERR
rErr=$(Rscript testScript.R 2>&1)

我到处寻找一种方法,只捕获变量的错误流,并在控制台上保持输出不变(输出和错误(,但我被卡住了。

试试这个

exec 5>&1
exec 6>&1
rErr=$(Rscript testScript.R 2>&1 1>&6 | tee /dev/fd/5)

测试脚本

$ cat test
#!/bin/bash
ls
ls sdfgds

使用此脚本进行测试

$ exec 5>&1
$ exec 6>&1
$ err=$(./test 2>&1 1>&6 | tee /dev/fd/5)
file  new_file  test  xml
ls: cannot access 'sdfgds': No such file or directory
$ echo "$err"
ls: cannot access 'sdfgds': No such file or directory

很快

使用未命名的fifos(警告,这将起作用,因为操作系统进行缓冲,所以只有当输出保持在64Kb以下时!(,有一种微妙的bash方式:

exec {HOLDERR}<> <(:)
ls -ld /t{mp,nt} 2>&${HOLDERR}
read -t 0 -u $HOLDERR && read -ru $HOLDERR errmsg
exec {HOLDERR}<&-

这必须输出类似以下内容:

drwxrwxrwt 4 root root 4096 Jan 01 1970 /tmp

并用类似于declare -p errmsg的东西弹出$errmsg

declare -a errmsg=([0]="ls: cannot access '/tnt': No such file or directory")

更进一步

exec {HOLDERR}<> <(:)
ls -ld /t{mp,nt} 2>&${HOLDERR}
errmsg=()
while read -t 0 -u $HOLDERR;do
read -ru $HOLDERR line
errmsg+=("$line")
done
exec {HOLDERR}<&-
printf "%sn" "${errmsg[@]}"

完全可用版本

trap SIGCHLD冲洗fifo。

printmsg() {
local out=()
out=("${errmsg[@]}")
errmsg=("${errmsg[@]:${#out[@]}}")
[ "${#out[@]}" -gt 0 ] &&
printf "Err: %sn" "${out[@]}"
}
checkmsg() {
local line
while read -u $HOLDERR -t 0 ;do
read -ru $HOLDERR line &&
errmsg+=("$line")
done
}
msg=()
trap checkmsg CHLD
exec {HOLDERR}<> <(:)

然后

ls -ld /t{mp,nt} 2>&${HOLDERR}

必须输出以下内容:

drwxrwxrwt 4 root root 4096 Jan 01 1970 /tmp

然后是:

printmsg

将显示

Err: ls: cannot access '/tnt': No such file or directory

最新更新