在Bash脚本文件中记录日志

5

我有一个非常庞大的脚本(其中包含大约4000行代码)。这是其中的一部分:

#!/bin/bash 


. ./functions > /dev/null 2>&1

some_function(){

while true
do

CHOICE=$(whiptail --menu "\n\n\n\n\n\n\n\n" --title "Tools" --nocancel $window 20 \
"1" "Option1" \
"2" "Option2" \
"3" "Option3" 3>&1 1>&2 2>&3)


case $CHOICE in

    1)echo $1 $2 $3;;
    2)echo $2 $1 $3;;                                       
    3)echo $3 $2 $1;;

esac
done
}



while true; do 
arr=()
for ((n=1; n<=$node_num; n++))
        do
        node+=($n NODE$n)
done


OPTION=$(whiptail --menu "\n\n\n\nPlease choose:\n\n\n" --title "tools" $window 20 "${node[@]}" \

case $OPTION in

        1) some_function 1 2 3 ;;  
        2) some_function 2 1 3 ;;
        3) some_function 3 1 2 ;;
esac
done

我想记录脚本中执行的命令。

目前为止我尝试过的是:

  1. #!/bin/bash -x --> this will log all the output , but will also "spam" the logs with unneeded information like variable values etc. However this seems to be the best way so far...
  2. I have tried #!/bin/bash -i , enabling history with set -o history . The disadvantage of this is it will log everything . When I call the function file for example it will log every single line as if it was executed .
  3. I have tried creating a log function :

    logthis(){
        ## print the command to the logfile
        echo "$(date) $@" >> $historyfile
        ## run the command and redirect it's error output
        ## to the logfile
        eval "$@" 2>> $historyfile
    }
    

    This seems to work most of the time. But when I do, for example:

    case $OPTION in
        1) logthis "some_function 1 2 3" ;;  
        2) some_function 2 1 3 ;;
        3) some_function 3 1 2 ;;
    esac
    

无法正常工作,因为我会丢失参数1、2、3。

你有其他想法在bash脚本中实现优雅的日志系统吗?


2
跟踪可以在脚本中使用 set -xset +x 来根据需要进行启用和禁用;这是选项 1 的更细粒度版本。 - chepner
1
选项2不需要“-i”选项;历史记录在交互式 shell 中默认启用,但不需要交互式 shell。 - chepner
是的,我可以启用和禁用调试模式,但我正在寻找更优雅的方法,而不是浏览所有代码并设置set -x / set +x。 - zlobul
1
这里是Bash日志记录的全面实现:https://github.com/codeforester/base/blob/master/lib/stdlib.sh - codeforester
2个回答

7

在你的日志函数中,摆脱 eval。只需编写 "$@" 执行传递的命令即可。

logthis() {
    echo "$(date): $@" >> "$historyfile"
    "$@" 2>> "$historyfile"
}

然后,您只需在命令前面添加 logthis 即可记录该命令。不需要额外的引号。

logthis some_function 1 2 3

这将非常好地保留所有参数,即使它们有空格或其他特殊字符。

我建议对echo命令进行微小的改进。如果您使用printf %q,它将更好地记录带有空格的参数。

echo "$(date):$(printf ' %q' "$@")" >> "$historyfile"

为什么最后一位不直接使用printf输出整个内容呢? - Mad Physicist
printf会为每个额外的参数重复格式字符串。无法在一个printf中同时执行这两个操作并打印日期。 - John Kugelman
不幸的是,当我尝试传递变量时,例如:logthis命令$1 $2,会导致某些问题发生,而whiptail窗口则无法显示... :/ - zlobul
如果你还有问题,你应该写一个针对whiptail的问题。我不熟悉它。 - John Kugelman
这是我找到的关于whiptail的链接:https://dev59.com/jXI-5IYBdhLWcg3wNla1,因此似乎whiptail正在切换fd,这使得在所有情况下都无法使用logthis函数。 - zlobul

2

尝试使用set -v

这不会像set -x一样解析命令,只会输出执行的内容。

set -v
: all the things 'in' $0, 'yay!'

输出精确地为: 所有在 $0 中的东西,'yay!'
甚至不解析 $0。

记录参数,最小化垃圾邮件。 ;)

考虑在代码主块周围加上花括号来管理输出日志。

{ # after setup to parse args, set vars & traps, declare funcs, etc
  your bulk code here
} 2>&1 | tee /some/file.log

你可以把 set -x 的冗长信息留给 --verbose 模式。 ;)

谢谢,但这仍然会记录所有的whiptail菜单,例如。我无法像变量声明一样将它们输出到/dev/null。 - zlobul
1
你能把菜单抽象成一个函数吗?这个函数在开始时关闭-v,在结束时打开它。你正在要求细粒度日志记录,所以恐怕你必须编写细粒度代码。 - Paul Hodges

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接