如何远程终止 bash shell 进程但确保历史记录被保存?

如何远程终止 bash shell 进程但确保历史记录被保存?

有时我的 X11 窗口管理器 (i3) 会锁定,我被迫切换到 Linux 虚拟控制台或通过 SSH 进入我的机器,以重新获得控制权。

通常当我打开多个 rxvt 窗口时会发生这种情况,每个窗口都运行一个 bash shell,其中包含几天或几周内收集的数十甚至数百行有用的命令行历史记录。

我使用shopt -s histappend这种方法,这样每个 bash shell 都会在有序终止时将其历史记录附加到历史文件中。通常情况下,这种方法效果很好,只要我用exit或 CTRL-D 干净地关闭每个 shell,它就会保留我的所有历史记录。

但是当我的窗口管理器锁定时,我无法干净地退出我的 bash 会话,因为没有办法与它们交互。因此,我一直在尝试寻找一种方法来远程终止 bash shell,以便将其历史记录写入历史文件。

我尝试SIGHUP向 bash shell 进程和包含的 rxvt 进程发送许多信号,包括,但没有一个导致历史记录保存。

我尝试过重启 i3,但由于各种原因,没有成功(似乎是 RPC 套接字损坏)。关闭窗口管理器会关闭所有窗口和 bash shell,我会丢失所有历史记录。

我试图找到一种方法将“CTRL-D”注入到每个 shell 中stdin,但据我所知这是不可能的。

我知道有方法让 bash 在执行完每个命令后将历史记录保存到历史文件中,但是我不想使用这些方法,因为它们会弄乱历史索引(即,!nnn由于每个命令后都会发生变化而无法工作nnn)。

有什么办法可以做到这一点并将我珍贵的历史从遗忘中拯救出来吗?

答案1

使用reptyr

reptyr是一个用于获取现有正在运行的程序并将其附加到新终端的实用程序。

我的这个答案了解详情。将所需的 shell 进程附加到当前终端后,您可以exit优雅地告诉它。

不过,目前可能很难识别您想要附加的特定 shell 进程。您可能会reptyr使用任何shell 进程ps(或类似工具),那么您将检查前面的命令,然后才知道它是否是所需的会话。

一种更方便的解决方案是使用tmux或者screen首先。

tmux我每天都会使用它,从 tty2 重新连接是一项简单的任务。使用该工具,我不需要“打开大量 rxvt 窗口”。通常我会这样组织我的工作:

  1. 单一konsole窗口。
  2. 每台机器一个选项卡(直接在本地主机上,再加上ssh一些远程主机),tmux每台机器上都有一个选项卡。
  3. 每个窗口内有tmux一个或多个(根据需要)。
  4. 每个窗口内有一个或多个窗格(根据需要)。

这些“层”在下面的屏幕截图中用相应的红色数字标记。X11 或 KDE 问题不会影响我的 shell 会话,只要我能访问任何给定机器的控制台。只有服务器本身崩溃tmux才会造成伤害,但我记不起上次发生这样的事情是什么时候了(可能永远不会发生)。

如果“我被迫切换到 Linux 虚拟控制台或通过 SSH 进入我的计算机以重新获得控制权”,我所需要的tmux a只是我的 shell 会话恢复正常,就像什么都没发生一样。如果我可以强制我的窗口管理器重新加载,那么我甚至不需要终止这些 shell 进程,因为我可以从新的 或任何终端仿真器返回到konsole它们rxvt

截屏

答案2

reptyr如果可用的话,这是一个令人惊叹的解决方案,但不幸的是情况并非总是如此。

当你HUP在重新连接后向丢失的 bash发送一个写出其历史记录。但通常情况下,您是HUP从另一个 bash 会话发送的,这会在退出时覆盖被终止的 shell 的历史记录。

您想要清除当前 shell 的历史记录(HUP丢失的 shell),给它一秒钟时间来清除其自己的历史记录,然后从磁盘重新加载历史记录:

$ history -a; kill -HUP 40705; sleep 1; history -r

您将按该顺序获得完整的历史记录,包括本地和死壳的历史记录。

已测试GNU bash 4.1.2(1)-release

相关内容