为了防止在 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
通过按Up和ENTER(即历史记录中的最后一个命令)不断重复。一旦第一个命令完成,历史记录中的最后一个命令就不再是ls
,而是cat /dev/foo > file.out
。如果我不小心,我会再次启动cat并覆盖file.out。
我想要实现的是 cat 命令前面带有 a #
,这样它就不会被执行。然而,我仍然会在历史中看到它,并且可以通过取消注释来重用它(如果它是一个长命令)。
答案1
我不会准确回答您的问题,但也许会为您的问题提供替代解决方案。
如果我理解正确的话,您会担心输入时可能会犯的错误,例如,!rm
如果rm
历史记录中的上一个命令删除了您想要保留的内容。
在这种情况下,一个好的巴什选项是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
答案2
你可以这样做:
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
设置)。
但它的一个不想要的副作用是它改变了历史时间其中rm
,mv
...命令。
为了解决这个问题,替代方案可能是:
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
。不幸的是,当前版本bash
有一个错误,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
去做。
答案3
这已接受 Stéphane 的回答绝对是惊人的。下面是我采用的它,它的模式匹配有点不同。
使用if
statements 而不是case
允许我使用正则表达式匹配 ( [[ "$cmd" =~ <REGEX> ]]
),下面的代码按以下逻辑工作:
- 在以下情况下不要添加到历史记录:
- 命令行以 2 个或更多连续空格开始或结束
示例:<spc><spc>history
- 命令行以 2 个或更多连续空格开始或结束
- 添加以下情况下注释的命令:
rm
或mv
带有 cli 开关的命令- 命令行以
:;
或 单个空格开头或以;:
示例结尾:
rm -R /tmp/somedir
echo "doing something dangerous I don't want to trip by accident";:
这也有鱼子酱,我对此很满意,例如:
echo ";rm -something"
- 否则添加到历史记录
示例:rm somefile
echo ";rm -something"
代码
_history_hook() {
# The code below is from https://unix.stackexchange.com/a/110056/138012
# history builtiun options used: https://www.gnu.org/software/bash/manual/html_node/Bash-History-Builtins.html
# history -a : Append new (mem) lines to history (file)
# history -d : Delete range (mem)
# history -c : Clear history list (mem) ++
# history -r : Read (file) append to (mem) ++
# ++ needed to circumvent a bug issue.
local cmd time histnum
cmd=$(HISTTIMEFORMAT='<%s>' history 1)
histnum=$((${cmd%%[<*]*}))
time="${cmd%%>*}"
time=${time#*<}
cmd="${cmd#*>}"
# different behaviors for different patterns
if [[ "$cmd" =~ ^\ \ +.*|\ +\ $ ]]; then
# delete only (two spaces - leading or trailing)
history -d "$histnum" # delete
elif [[ "$cmd" =~ ((^|;[[:space:]]*)(rm|mv)\ +-)|^\ |^:\;|\;:$ ]]; then
# rewrite history as a 'safe version'
history -d "$histnum" # delete
history -a
[ -f "$HISTFILE" ] && printf '# %s\n' "$time" "$cmd" >> "$HISTFILE"
else
# append 'as usual'
history -a
fi
# rewriting history file
history -c
history -r
}
export HISTCONTROL=erasedups # This does not seem to work right now - need to fix this
export PROMPT_COMMAND="$PROMPT_COMMAND; _history_hook"