捕获一行代码的错误流,并使用trap报告错误流,同时保持标准输出不变 很快再走一步完整可用版本

问题描述

我在下面的2个Shell脚本中尝试了以下(简化的)代码

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

我要实现的是照常在控制台上同时显示输出错误流,但是当脚本运行且失败时(例如RScript产生错误流),我想保存该错误消息R在sqlite数据库生成,但仍具有正常输出,并且退出时控制台上也会显示错误。我已经尝试了多种形式的Rscript函数输出重定向,但是我要么最终没有将任何东西保存到数据库中(除了行号),要么我可以保存错误消息,但是控制台上什么也不会放置... >

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

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

这会将错误消息保存到数据库,但是控制台上将不再有任何内容

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)

我到处寻找一种方法来仅捕获错误流到变量并保持控制台上的输出不变(输出错误),但是我被困住了:)

Grtz, PJ

解决方法

尝试一下

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 (警告,因为OS会进行缓冲,所以这将起作用,因此仅当输出保持在64Kb以下时才可以使用!),这是一种微妙的方式:

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

并使用以下内容填充$errmsgdeclare -p 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 "%s\n" "${errmsg[@]}"

完整可用版本

使用trap SIGCHLD刷新fifo。

printmsg() {
    local out=()
    out=("${errmsg[@]}")
    errmsg=("${errmsg[@]:${#out[@]}}")
    [ "${#out[@]}" -gt 0 ] &&
    printf "Err: %s\n" "${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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...