每次登录时 BASH 历史记录截断为 500 行

每次登录时 BASH 历史记录截断为 500 行

由于某种原因,我无法让系统在重启后保留 BASH 历史记录。以下是我的相关部分~/.bashrc

shopt -s histappend
PROMPT_COMMAND='history -a; updateWindowTitle'
export HISTCONTROL=ignoredups
export HISTSIZE=9999
export HISTFILESIZE=999999
export HISTFILE="$HOME/.bash_history"

据我所知,这些都是必要的选择(我知道以前,即使没有这些选项,我也能够在多次重启后保留历史记录。然而,尽管我在几次重启前添加了这些选项,但重启后我仍然会丢失大部分历史记录。它不是空的,但没有我在重启前拥有的 9999 行。

在有人抱怨之前,是的,我已经阅读了这些问题。我已经实施了上面列出的一些建议,其余的要么没有帮助,要么不相关:

如果还有其他相关命令,你可以查看我的整个~/.bashrc 这里

那么,我遗漏了什么?为什么我的历史记录没有保存?如果有人认为另一个文件可能相关,请告诉我,我会发布它。我通过运行grep -i hist \.*我的文件进行了检查$HOME,结果显示唯一.包含字符串hist或的相关文件HIST.bashrc

我正在运行 Linux Mint Debian 版本、GNU bash、版本 4.2.36(1)-release(x86_64-pc-linux-gnu),我最喜欢的终端仿真器(如果相关的话)是terminator


更新:

根据评论中@mpy 的建议,我将~/.bashrc设置更改HISTFILE=~/bash_history为默认设置~/.bash_history,这似乎解决了交互式外壳。登录 shell 仍显示相同的行为,历史记录在行处被截断。但是,相关文件中500没有设置相关变量:HIST

$ for f in /etc/profile ~/.profile ~/.bash_profile ~/.bash_login; do \
   echo -ne "$f :"; echo `grep HIST $f`; \
done
/etc/profile :
/home/terdon/.profile :grep: /home/terdon/.profile: No such file or directory
/home/terdon/.bash_profile :grep: /home/terdon/.bash_profile: No such file or directory
/home/terdon/.bash_login :grep: /home/terdon/.bash_login: No such file or directory
$ grep -r HIST /etc/profile.d/  <-- returns nothing

那么,为什么设置HISTSIZEHISTFILESIZE~/.bashrc不够的,除非我明确将设置$HISTFILE为默认值以外的其他值~/.bash_history

答案1

问题实际上归结为不同的行为登录和非登录 shell。我已在 中设置了控制历史记录的变量~/.bashrc。启动登录 shell 时不会读取此文件,只有交互式非登录 shell 才会读取它(来自man bash):

当 bash 作为交互式登录 shell 或使用 选项作为非交互式 shell 调用时--login,它首先从文件 读取并执行命令/etc/profile(如果该文件存在)。读取该文件后,它会按顺序查找 ~/.bash_profile、 ~/.bash_login~/.profile,然后从第一个存在且可读的文件中读取并执行命令。--noprofile启动 shell 时可以使用 选项来禁止此行为。

[。 。 。 ]

当启动非登录 shell 的交互式 shell 时,如果 ~/.bashrc 文件存在,bash 将从该文件中读取并执行命令。可以使用 --norc 选项禁止此操作。--rcfile 文件选项将强制 bash 从文件而不是 ~/.bashrc 中读取并执行命令。

因此,每次我登录、切换到 tty 或使用 ssh 时,文件.history都会被截断,因为我没有将其设置为正确的大小~/.profile。我终于意识到了这一点,只需在~/.profile 他们属于哪里, 代替~/.bashrc

因此,我的文件被截断的原因~/.history是因为我只在由交互式非登录 shell 读取的文件中设置了 HISTORY 变量,因此每次我运行不同类型的 shell 时,变量都会被忽略,文件也会被相应地剪切。

答案2

我的建议是使用另一个文件HISTFILE,而不是默认文件~/.bash_history

虽然我没有分析性的解释,但我会尝试概述是什么促使我提出这个建议:如果您将bash其用作默认(登录)shell 并且还使用X(两者都很有可能)则bash在(图形)登录后立即有一个正在运行的实例:

systemd
 ...
  |-login
  |   `-bash      <<====
  |       `-slim
  |           |-X -nolisten tcp vt07 -auth /var/run/slim.auth
  |           |  `-{X}
  |           `-fluxbox
  |               `-xterm -bg black -fg white
  |                   `-bash
 ...

我认为这个实例是一个登录 shell,所以它不会读取您的信息~/.bashrc,因此不会对该histappend选项有任何了解:

男子猛击(1):当交互式 shell不是登录shell 启动后,bash 从 /etc/bash.bashrc 和 ~/.bashrc 读取并执行命令(如果这些文件存在)。(...)

只要这个“父 shell”运行,一切都会正常,但是当它终止时(即系统停止),它将覆盖~/.bash_history(因为这是默认值)并弄乱您的历史记录或在系统启动时将其剪辑到(再次默认)500 行。(或者两者兼而有之......)

我也觉得,将历史配置包含在内是不够的~/.bashrc,因为这不应该是一种不常见的设置。对此我没有任何解释。


关于您的问题,“登录 shell 仍然显示相同的行为”,您可以尝试在以下内容中包含历史配置~/.bash_profile

男子猛击(1):当 bash 作为交互式登录 shell 或使用 --login 选项作为非交互式 shell 调用时,它首先从文件 /etc/profile 中读取并执行命令(如果该文件存在)。读取该文件后,它会查找 ~/.bash_profile,(...)

不幸的是,我无法根据我自己的bash配置提供更合理的解释和详细信息,因为我是一个zsh男性……

答案3

由于您的所有设置都符合手册页的顺序,并且历史文件不受大小(字节)限制,所以我能想到的唯一可能的解释是。它与 shell 如何死机有关。

根据在线参考,只有当 shell 收到 SIGHUP 时,才会正常退出(保存历史记录)。我无法真正解释您的系统在重新启动时如何传播信号,但我怀疑您的 shell 会使用 SIGKILL 或 SIGPWR 退出。

这可能是因为您的 WM 异步运行(等待),并且从 Bash 所在的 WM 生成的终端仿真器会收到强制退出信号(而不是 SIGHUP)。也可能是因为操作系统在初始的 SIGHUP 设法通过 X -> WM -> xterm 到达 shell 之前就快速向所有进程发送了“最终终止”,这可能是因为 X 或 WM 退出所需的时间比操作系统准备关闭所需的时间要长。

我对这些东西深感困惑,但我认为类似这样的问题导致了这种不稳定的行为。我以前遇到过这个问题,最可靠的补救措施是exit在 bash 中保留历史记录。

我注意到了history -a你的问题,我想不出为什么这不足以保留历史记录。

您可以通过找出真正导致 bash 死亡的原因,然后找出信号的来源并在那里解决问题来解决问题,或者当您知道哪个信号是最后一个信号时简单地刷新历史记录(假设磁盘到那时仍然在线):

trap "echo got 1  >/tmp/sig1;  exit" SIGHUP
trap "echo got 2  >/tmp/sig2;  exit" SIGINT
trap "echo got 15 >/tmp/sig15; exit" SIGTERM
 .. and so on...

附带的截图说明了我在第二段和第三段中所说的内容。那里的顺序是我在 shell 中从左边,从右侧杀死左侧的 shell 并捕获历史记录。

男子猛击

在启动时,(...) 如果有必要,将截断由 HISTFILE 的值命名的文件,以包含不超过由 HISTFILESIZE 的值指定的行数(+ 默认值 500)。

如果启用了 histappend shell 选项(+ 此处为默认),则行将附加到历史文件,否则历史文件将被覆盖。

在线参考

3.7.6 信号

当 Bash 处于交互状态时,如果没有任何陷阱,它会忽略 SIGTERM(因此“kill 0”不会终止交互式 shell),并且会捕获和处理 SIGINT(因此 wait 内置命令是可中断的)。当 Bash 收到 SIGINT 时,它会跳出任何正在执行的循环。在所有情况下,Bash 都会忽略 SIGQUIT。如果作业控制有效(请参阅作业控制),Bash 会忽略 SIGTTIN、SIGTTOU 和 SIGTSTP。

Bash 启动的非内置命令的信号处理程序设置为 shell 从其父级继承的值。当作业控制无效时,异步命令除了忽略这些继承的处理程序外,还会忽略 SIGINT 和 SIGQUIT。作为命令替换结果运行的命令会忽略键盘生成的作业控制信号 SIGTTIN、SIGTTOU 和 SIGTSTP。

默认情况下,shell 在收到 SIGHUP 后退出。在退出之前,交互式 shell 会向所有正在运行或已停止的作业重新发送 SIGHUP。已停止的作业将收到 SIGCONT 以确保它们收到 SIGHUP。要防止 shell 向特定作业发送 SIGHUP 信号,应使用 disown 内置命令将其从作业表中删除(请参阅作业控制内置命令),或使用 disown -h 将其标记为不接收 SIGHUP。

如果已使用 shopt 设置了 huponexit shell 选项(请参阅内置 Shopt),则当交互式登录 shell 退出时,Bash 会向所有作业发送 SIGHUP。

如果 Bash 正在等待命令完成并收到已设置陷阱的信号,则陷阱将不会执行,直到命令完成。当 Bash 通过内置 wait 等待异步命令时,收到已设置陷阱的信号将导致内置 wait 立即返回,退出状态大于 128,之后立即执行陷阱。

演示屏幕截图

信号

答案4

检查 /etc/profile 和 /etc/profile.d/*

也许那里的历史设置有些混乱。

相关内容