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


22

由于某种原因,我无法让系统在重启后保留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该结果显示唯一相关的.文件包含字符串histHIST.bashrc

我正在运行Linux Mint Debian Edition,GNU bash,版本4.2.36(1)-发行版(x86_64-pc-linux-gnu),而我最喜欢的终端模拟器是(如果相关)terminator


更新:

遵循@mpy在评论中的建议,我~/.bashrc将set 改为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

那么,除非我明确将default 设置为默认值,否则为什么set HISTSIZEHISTFILESIZEin ~/.bashrc还不够?$HISTFILE~/.bash_history


您是.bash_history或root的所有者?ls -l .bash_history
Adnan Bhatti

1
@MSStp是的,它归我​​所有。感谢您的建议,但无论如何我都看不到它可能是权限问题,无论我是否具有读/写访问权限。由于保存了一些历史记录,因此我显然可以这样做。如果bash的设置在用户不拥有此文件时导致出现问题,则它可能会抱怨或整个历史记录功能将无法使用。
terdon

当执行history命令时,cat .bash_history除了行号以外,您看到的输出与运行中的输出相同。我的意思是history命令列出时间戳或其他信息吗?我要问的原因是,如果您看到这些深奥的内容,则意味着还有另一个模块/功能/程序,这与shell历史记录混淆,并且其内容存在错误或错误的版本,可能会导致您感到悲伤。
MelBurslan

@Mel_Burslan是的,是相同的,唯一的不同是行号。
2013年

2
好的,有点奇怪的建议,但这也是一个奇怪的问题;):尝试将另一个文件作为HISTFILE而不是default ~/.bash_history。非常有条理的解释:我假设bash是您的默认外壳程序,因此在系统启动时,非交互式外壳程序是X会话的父级(我也假设您使用X),这对histappend选项一无所知(因为.bashrc仅(由交互式外壳读取),因此只要此父外壳运行,一切都很好,但是在终止(即系统停止)时,它将覆盖~/.bash_history(默认)并弄乱您的历史记录……
mpy

Answers:


17

问题实际上归结为登录和非登录外壳的不同行为。我已经设置了控制历史记录的变量~/.bahsrc。当启动登录shell时不会读取此文件,只能由交互式非登录shell(从man bash)读取:

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

[。。。]

启动不是登录外壳程序的交互式外壳程序时,如果该文件存在,则bash从〜/ .bashrc读取并执行命令。使用--norc选项可以禁止这种情况。--rcfile file选项将强制bash从文件而不是〜/ .bashrc中读取并执行命令。

因此,每次我登录,放下tty或使用ssh时,.history文件都会被截断,因为我也没有将其设置为正确的大小~/.profile。我终于意识到了这一点,只需将变量设置~/.profile 在它们所属的位置,而不是~/.bashrc

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


很好,谢谢分享!但是,我不同意:启动登录shell时不会读取此文件,它只能由交互式shell读取。因为登录外壳也可以是交互式的。否则,整个历史记录机制将毫无意义。.bashrc无法读取IMHO (非交互式或登录Shell)。
mpy

@mpy确实,抱歉,我的意思是交互式非登录外壳。答案已编辑。
terdon

1
@terdon,常见的成语是交互式shell选项不在〜/ .profile或〜/ .bash_profile中。交互式shell选项放在〜/ .bashrc中。为了避免必须在两个地方维护设置,可以在〜/ .bash_profile的顶部放置以下命令:export BASH_ENV=~/.bashrc ; if [ -f ~/.bashrc ]; then . ~/.bashrc; fi...在〜/ .bashrc的顶部放置一个检查以确保您确实在交互式运行:[ -z "$PS1" ] && return...当然,这只是一个成语。
Noah Spurrier 2014年

1
@NoahSpurrier BASH_ENV不相关,它仅影响非交互式外壳。至于“惯用语”,这是Debian开始的,我个人不同意。我的xset.bashrc中经常有图形选项(等等),当我从tty或through运行登录shell时,我不希望这些选项处于活动状态ssh。我希望我的.profile和.bashrc分开。许多(尽管不是全部)登录管理器在您登录时都使用.profile,因此最好在此处设置全局变量,在该全局变量中,将只读取一次,而不是每次打开终端时都读取它们。
terdon 2014年

1
@WilsonF是的。依次读取文件,最后读取个人文件(~/.profile~/.bashrc)。这些设置中的任何设置都将优先于全局设置。没错,您不应该在中设置这些变量/etc/bash.bashrc。使用~/.profile(或~/.bash_profile)代替。
terdon

11

我的建议是使用另一个文件作为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选项有所了解:

man bash(1):启动不是登录 shell 的交互式shell 时,bash会从/etc/bash.bashrc和〜/ .bashrc中读取并执行命令(如果存在)。(...)

只要此“父外壳”运行,一切都很好,但是在终止(即系统停止)时,它将覆盖~/.bash_history(因为这是默认值)并弄乱您的历史记录或在系统启动时将其裁剪为(再次默认)500线。(或者也许两者都...)

我也感到很惊讶,仅在中包含历史记录配置是不够的~/.bashrc,因为这不是一个罕见的设置。我对此没有任何解释。


关于您的问题,“登录外壳仍然显示相同的行为”,您可以尝试在~/.bash_profile以下位置也包括历史记录配置:

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

不幸的是bash,由于我是zsh个人,我无法发布更合理的解释,其中包含我自己的配置中的详细信息。


2
在我的显式设置历史记录选项中~/.bash_profile解决了该问题。我现在将其~/.bash_history用作历史记录文件,但只是将~/.bashrc问题中显示的所有行添加到中~/.bash_profile。仍然不确定谁在搞砸交互式shell,但是现在看来可以了,谢谢!
terdon

1
抱歉,我无法接受,但是我忘了发布答案以解释发生了什么。不久前,我在@Gilles的帮助下弄清楚了这一点,终于找到答案了。我想接受我的观点,因为它实际上是在解释问题,而不是提供(很好)解决方法,并且可以帮助将来的访问者。
0:09的terdon

2

由于所有设置均按照手册页排列,并且历史文件不受大小(字节)的限制,因此我能想到的唯一可能的解释是。它与外壳的死方式有关。

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

这可能是因为您的WM异步运行(等待),并且从bash所在的WM生成了终端仿真器,而不是收到SIGHUP以外的退出强制信号。也可能是OS会在初始正常的SIGHUP设法通过X-> WM-> xterm到达外壳之前迅速将“最终终止”发送到所有进程,这可能是因为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...

随附的屏幕快照说明了我在第二段和第三段中所说的内容。该序列有我在外壳,杀右和猫的历史左壳。

猛男

在启动时,如果需要,将(...)以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。

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

如果已经使用shopt设置了huponexit shell选项(请参见内置Shopt),则Bash在退出交互式登录Shell时将SIGHUP发送到所有作业。

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

示范屏幕截图

信号


在该屏幕截图中,我并没有真正了解您在做什么。什么是/tmp/pso你正在扼杀?我理解您关于不同的杀死信号的观点(尽管您说过,我认为那是history -a要解决的问题)。我将测试一段时间,然后报告。
terdon

$猫TMP / PS * \ n PS -o PID = |头-N1>〜/ tmp目录/ PSO \ n 17201 \ n
ЯрославРахматуллин

0

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

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


谢谢,但grep -r HIST /etc/profile.d/什么也没返回,我已经检查过了/etc/profile
terdon 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.