为什么我可以更改 bash 历史记录?

为什么我可以更改 bash 历史记录?

为什么bash即使我没有执行正在修改的命令,历史记录也会更改?例如,如果我输入:

$ echo foo1
foo1
$ echo foo2
foo2
$ echo foo3
foo3

之后,我按up两次,然后出现echo foo2提示。我按下2并得到echo foo22。现在回到末尾(空行) downdown。如果我现在搜索历史记录,echo foo22即使我从未执行过该命令,我也会看到!

但是,当我退出 shell 并打开另一个 shell 时,我会看到原始执行的命令。我发现这一切非常令人困惑、违反直觉且令人恼火。

我希望历史的事情像这样工作:

  1. 只有实际执行的命令才会保存到历史记录中。

  2. 历史是不可改变的。 (除非明确清除。)

有办法做到这一点吗?

编辑-这个问题似乎相关:有什么办法可以撤消 bash 历史记录修改吗?

答案1

我发现这一切非常令人困惑、违反直觉且令人恼火。

这是你的意见;我为一种编程语言实现了一个交互式 REPL,其中每一行历史记录不仅可以编辑,而且有自己独立的撤消历史记录。因此,尽管可能存在从未运行过的命令,但您可以使用向上箭头返回到该命令并将其撤消回原始状态。

然而,就像 Bash 中一样仅当您离开修改的行时,历史记录的编辑才会保存到历史记录中。如果您编辑该行然后将其作为新命令重新提交而无需导航离开它,则不会!在这种特殊情况下,编辑不会保存到历史记录中,因为它们被保存为新的历史记录条目。

这是有规律和理由的!

  1. 在将历史行作为新命令提交之前不允许对其进行编辑,这显然是违反直觉且令人恼火的。

  2. 如果您离开历史行查看另一行,那么简单地丢失对历史行的编辑将是违反直觉且令人恼火的。

一个可能的解决方案可能是这样的:当用户编辑历史记录行时,将其保存在代表新要提交的行的临时区域中(并且不更新历史记录)。然而:

  1. 这仍然是违反直觉且令人恼火的。用户将无法编辑一行,请按两次向上箭头查看上一行,然后按两次向下箭头返回编辑。然后,用户将看到原始的未编辑行,并且必须一直导航到历史记录的前面,才能在当前(“历史记录零”)编辑缓冲区中找到该行的新版本。

  2. 如果用户在两个不同的历史记录行中进行了两次编辑怎么办?如果最近的此类编辑被最新的编辑悄然破坏,那将是令人恼火的。

拥有不可变历史记录的唯一方法是,您可以同时进行多个临时编辑,并以任何顺序将它们作为新命令提交,但这种方法可能很难在当前的 UI 范例中呈现而不造成混淆。 “当前 UI 范例”是整个工作区(历史记录加上新条目)映射到屏幕上的单个编辑窗口,并带有视图(如果有)关于用户所在位置的任何视觉线索以及任何状态指示。

我们可能需要一个位置指示器(一些小数字,向用户指示他们在历史空间中的位置),指示该历史行是否包含临时修改;一种在查看修改或底层未更改行之间切换的方法;一种使修改永久生效的方法(改变历史记录);放弃对一行或一系列行或所有行等的修改的方法。

答案2

我确实同意,这是一种令人讨厌的默认行为(并且它似乎是bash/所独有的readline)。至少根据我自己的经验,如果我放弃修改过的行,通常是因为我已经放弃了这些修改,并且宁愿将它们丢弃也不愿失去原始行!

如果您放弃修改该行的原因是因为您必须先做其他事情,那么将这些修改保留在那里可能会很有用,而这可能是这样做的原始理由。不过,在我看来,覆盖原始历史条目是不好的,因为你会丢失以前做过的事情的证据。

最烦人的情况是,您最终清除整行以重新启动新命令,然后转到您接受的另一行,并稍后意识到一个历史记录条目似乎已经消失。

此外,即使您打算保留这些修改,如果您想返回到修改后的行,您也需要再次搜索它。

IMO的行为zsh更有用。只要您不接受一行并在修改的行之间来回切换,您就可以修改整个历史记录,但是一旦您接受新行,它就会添加到历史记录中,但旧的历史记录行将被保留(同时按Ctrl+C取消所有修改而不向历史记录添加新行)。

如果你想保存对不同行所做的修改,你可以将该行存储在一个队列中Alt+Q(在下一个提示时会自动拉出队列的顶部),或者将其存储在杀环Ctrl+U

值得庆幸的是,在bash/中readline,您可以使用以下命令更改行为

bind 'set revert-all-at-newline on'

(或者set revert-all-at-newline on因此~/.inputrc它适用于使用 readline 的所有应用程序)。

然后,您将得到与 中类似的行为yash,与 相同zsh但没有抓住队列(但您仍然可以使用杀环(用Ctrl+Y, Alt+Y) 拉动。

答案3

默认情况下,按下和分别调用previous-historynext-historyreadline 函数。

这些功能允许您从历史记录中获取行并编辑历史记录。

如果将和绑定到 和history-search-backwardhistory-search-forward您会得到类似的行为,而无需编辑历史记录的风险。

为此,请将其添加到您的~/.inputrc

# Press up-arrow for previous matching command
"\e[A":history-search-backward
# Press down-arrow for next matching command
"\e[B":history-search-forward

或者将其添加到您的~/.bashrc

# Press up-arrow for previous matching command
bind '"\e[A":history-search-backward'
# Press down-arrow for next matching command
bind '"\e[B":history-search-forward'

这将稍微改变方式工作方式。
当命令行为空时,该行为与原始函数相同 - 您可以通过历史记录进行标准单步执行(除非没有历史记录编辑)。
当您输入某些内容然后按 时,bash 会将您输入的任何内容视为前缀,并且仅显示历史记录中的匹配命令。

例如,键入git并按下将显示历史记录中以 开头的命令git按空命令行会显示所有命令。

相关内容