unix 记录所有shell命令执行时的退出代码

6mzjoqzu  于 5个月前  发布在  Unix
关注(0)|答案(3)|浏览(53)

我想记录所有shell命令的退出代码从一个特定的TTY没有额外的语法每个命令。
举例来说:

> source solution.sh # sets up this bash session to monitor exit codes
> ls
> ls f
ls: f: No such file or directory
> echo "hello world"

字符串
一些文件将存储退出代码:

0
1
0


这可能吗?如果不可能,我愿意接受其他可能实现类似目标的想法。
我的最终目标是收集所有已执行命令及其退出代码的数据(有点像附加了退出代码的~/.bash_history

dba5bblo

dba5bblo1#

感谢大量有用的评论,我已经达到了以下解决方案,以记录shell命令执行时的退出代码:
1.使用陷阱调试:

trap 'x=$?; echo "$x" >> ~/exit-codes.log' DEBUG

字符串
1.使用PROMPT_COMMAND

PROMPT_COMMAND='x=$?; echo "$x" >> ~/exit-codes.log; (exit $x)'


请注意,PROMPT_COMMAND必须使用退出代码退出,以便在shell命令中保留退出代码历史记录。

  1. Process accounting是一个更有原则的解决方案,但它需要sudo访问才能启用。
    我最终使用solution.sh记录退出代码 * 和 * 命令如下:
trap 'previous_exit_code=$?; previous_command=$this_command; \
  this_command=$BASH_COMMAND; [ ! -z "$previous_command" ] && \ 
  echo \"$previous_command\", $previous_exit_code >> ~/exit-codes.log' DEBUG

falq053o

falq053o2#

我真的很喜欢@ben-albrecht answer,我只是花了一些时间调试/调整它(因为我不喜欢任何更好的历史记录方法),所以我想做出贡献。
变体:PROMPT_COMMAND='x=$?; echo $x >> ~/exit-codes.log; (exit $x)'有一个问题:如果你想记录整个命令,而不仅仅是exitcode,你不能访问$BASH_COMMAND,因为你在这个变量中输入的任何命令都会被覆盖,所以你会丢失原来的命令。如果你使用history 1,它会工作,但是如果你输入'empty'命令3次,bash历史不会被更新,history 1将返回相同的东西3次,您的历史日志将包含重复的内容。
带有陷阱的变体还有另一个问题,那就是泄漏变量。在其中设置的变量将可以在外部访问,这可能会破坏此功能或其他功能。下一个问题是,该命令仅在调用下一个命令后才被记录。然后是gotcha:如果你在.bashrc文件中设置了这个,它需要在最后一行,否则所有剩余的命令都会被记录下来,就好像你调用了它们一样。即使这样,会有一些你不想在日志中看到的记录命令,例如在我的例子中的fzf助手函数。
所以我是这样做的。在.bashrc中我声明了一个函数,这个函数被trap --> no variable leaking调用。

function record_permanent_history() { ec=$?;echo -e "$(date +%FT%T.%3N)\t${ec}\t${1}\t${PWD}" >> ~/.bash_history_permanent; return $ec;  }
trap 'record_permanent_history "${BASH_COMMAND}" ' DEBUG

字符串
显示的代码生成以下行:

2023-12-06T20:11:37.541 0   return $ec  /home/me


字段由制表符分隔,字段的顺序是故意的,并解决了一些问题:
1.第一列是时间戳,以便在需要时更快地订购。
1.第二列是退出代码
1.第三列是实际指挥。
1.第四列是当前目录(可能是浪费,但有时有人有很好的命令,但它不工作,因为当前目录,这是很难找出原因)。
因为fzf需要这种顺序。fzf有一些特定的分隔符,而不是空格,并且空格/其他字符可以包含在命令本身中。通过这种对齐,我们可以得到这个脚本:

#!/bin/bash

OUT="$(fzf -d "\t" --scheme=history --with-nth 3..-2 --preview="echo 'time={1}'; echo 'ret={2}';echo dir='{-1}';echo;echo command;echo {3..-2}" --preview-window=right:wrap --expect=alt-enter < ~/.bash_history_permanent)"

KEY=$(echo "$OUT" | head -n 1)
CMD=$(echo "$OUT" | tail -n 1 | sed "s/^[^\t]*\t[^\t]*\t\(.*\)\t[^\t]*$/\1/")

if [[ -n "${KEY}" ]]; then
        case "${KEY}" in
                alt-enter) eval "${CMD}"  ;;
        esac
else
        echo ${CMD};
fi


这将打印你所有的信息很好地提出,与模糊搜索,输入打印选定的命令到标准输出,alt输入执行它。
限制:

  • 直到fzf的字段访问得到改善,你(可能)运气不好,目录名和命令不能包含实际的制表符。2应该没有问题。
  • fzf的作者不知何故实现了键入命令而不是打印到标准输出。我还需要理解这段代码并“窃取”它。

问题:

  • trap命令的使用是次优的。它记录函数内部(如图所示),以及其他命令,即来自.bashrc的命令,但令人惊讶的是,不是来自自定义脚本的单个命令。别名在日志中被替换,这是真正不需要的。

作为第一个增量可能已经足够好了。我需要将陷阱重写为PROMPT_COMMAND并使用命令ID检测重复,这对于一个暴露的env变量来说是很容易做到的。当我有更好的解决方案时,我会更新这个答案。

gmxoilav

gmxoilav3#

最后一个退出代码存储在$?变量中。
但是你必须检查它的每个发出的命令,所以仍然需要一些胶水来实现你的目标,我猜。

相关问题