bash_history:注释掉危险的命令:#


16

为了防止在bash历史记录中记录“危险”命令,我在.bashrc文件中添加了以下行:

HISTIGNORE='rm *:mv *:cp *:cat*>*:pv*>*'

这行之有效,但有副作用:我看不到在计算机上执行的命令的完整历史记录。假设我有几台用于实验的机器,并且希望能够看到所有执行的命令。我将使用bash内部history显示执行的命令,也许还可以显示今天的日期的grep:

history | grep Sep-28

我想做的是也记录“危险”命令,但是#在行的开头放置一个,这样,如果我偶然从历史记录中错误地执行了该命令,将不会造成任何损失。

我不知道这是否可能。

更新和说明:

对我来说这是个问题的主要原因是,我通常从多个终端连接到我的机器,并且在一个终端上执行的任何命令都会立即读入其他终端的历史记录中。这是通过实现

PROMPT_COMMAND="history -a; history -c; history -r"

假设我有两个开放的终端。在一个我有一些cat /dev/foo > file.out进程正在运行。在第二个中,我使用查看进度ls -lAhF。我一直ls通过按UpENTER(即历史记录中的最后一条命令)重复操作。第一个命令完成后,历史记录中的最后一个命令将不再是ls,而是cat /dev/foo > file.out。如果我不小心,我将再次启动cat并覆盖file.out。

我想实现的是cat命令将以开头#,这样它就不会被执行。但是,我仍然会在历史记录中看到它,并且可以通过取消注释它来重用它(如果这是一个长命令)。


您可以使用watch ls -lAhFwhile sleep 1; do ls -lAhf; done; 而不是手动重复命令。无需查看文件大小,您可以使用pv /dev/foo > file.outivarch.com/programs/pv.shtml)。
deltab 2014年

Answers:


9

您可以执行以下操作:

fixhist() {
   local cmd histnum
   cmd=$(HISTTIMEFORMAT=/ history 1)
   histnum=$((${cmd%%[*/]*}))
   cmd=${cmd#*/} # remove the histnum
   case $cmd in
     (rm\ *|mv\ *|...)
       history -d "$histnum" # delete
       history -s "#$cmd"    # add back with a #
   esac
}
PROMPT_COMMAND=fixhist

这样的想法是,在每个提示之前,我们检查最后一个历史记录条目(history 1),如果它是危险的条目之一,我们将其删除(history -d),然后使用#with 将其添加回去history -s

(显然,您需要删除HISTIGNORE设置)。

,虽然中不想要的副作用是,它改变了历史时间的那些rmmv...命令。

要解决此问题,替代方法可能是:

fixhist() {
   local cmd time histnum
   cmd=$(HISTTIMEFORMAT='<%s>' history 1)
   histnum=$((${cmd%%[<*]*}))
   time=${cmd%%>*}
   time=${time#*<}
   cmd=${cmd#*>}
   case $cmd in
     (rm\ *|mv\ *|...)
       history -d "$histnum" # delete
       HISTFILE=/dev/stdin history -r <<EOF
#$time
#$cmd
EOF
   esac
}
PROMPT_COMMAND=fixhist

这次,我们记录最后一次历史记录的时间,并添加回历史记录行,我们使用history -r包含时间戳记的临时文件(此处文档)。

您希望fixhist先执行history -a; history -c; history -r。不幸的是,当前版本的bashbug history -a不能保存我们添加的额外行。解决方法是改为编写它:

fixhist() {
   local cmd time histnum
   cmd=$(HISTTIMEFORMAT='<%s>' history 1)
   histnum=$((${cmd%%[<*]*}))
   time=${cmd%%>*}
   time=${time#*<}
   cmd=${cmd#*>}
   case $cmd in
     (rm\ *|mv\ *|...)
       history -d "$histnum" # delete
       history -a
       [ -f "$HISTFILE" ] && printf '#%s\n' "$time" "$cmd" >> "$HISTFILE";;
     (*)
       history -a
   esac
   history -c
   history -r
}
PROMPT_COMMAND=fixhist

那就是将注释的命令自己附加到HISTFILE而不是让它history -a这样做。


你能解释一下cmd=${cmd#*[0-9] }吗?那我应该放在fixhist哪里PROMPT_COMMAND呢?目前,该文件已包含PROMPT_COMMAND="history -a; history -c; history -r"
Martin Vegter,2014年

的作用是HISTTIMEFORMAT=/什么?我已经定好了export HISTTIMEFORMAT="%b-%d %H:%M "。顺便说一句,当我将您的代码添加到我的代码时$HOME/.bashrc,什么也没发生。
Martin Vegter 2014年

HISTTIMEFORMAT=/仅用于一次调用history以获取输出,例如123 /rm x更容易提取cmd的输出。在这种情况下,bash哪里存在$HISTCMD伪造的某些版本存在问题,请尝试新版本。
斯特凡Chazelas

仍然无法正常工作。我想知道,#代码中的不会充当中的注释.bashrc吗?
Martin Vegter 2014年

1
@MartinVegter,是的,对于已修改的历史记录条目,在期望的历史记录编号后bash插入一个*预期的数字。现在应该修复。
斯特凡Chazelas

11

我不会完全回答您的问题,但也许可以为您提供替代解决方案。

如果我理解正确,那么您担心键入时可能会犯的错误,例如,历史记录中!rm的前一个rm命令是否删除了您想保留的内容。

在这种情况下,不错的bash选项是histverify。如果你shopt -s histverify,并且用bang调用命令,该命令!将不会立即执行,而是会加载到readline中,以便您可以决定是否执行该命令,这也使您可以对其进行编辑。

尝试一下:

  • 没有histverify

    $ touch some_foo
    $ rm some_foo
    $ touch some_foo
    $ !rm
    rm some_foo
    $ # oooops in fact I'd've like to keep it this time
  • histverify

    $ shopt -s histverify
    $ touch some_foo
    $ rm some_foo
    $ touch some_foo
    $ !rm
    $ rm some_foo <cursor here>

    在这种情况下,您将光标置于行的末尾,可以再次启动它(或不启动)或进行编辑。

如果您喜欢此选项,请将其放在您的中.bashrc

shopt -s histverify
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.