我希望在历史记录中保存每个命令被执行时的当前目录。为了不搞砸事情,我考虑在每行命令结尾添加当前目录作为注释。以下是一个例子:
$ cd /usr/local/wherever
$ grep timmy accounts.txt
我希望bash能够保存最后一条命令为:
grep timmy accounts.txt # /usr/local/wherever
这个想法是这样的,我可以立即看到我发出命令的位置。
我希望在历史记录中保存每个命令被执行时的当前目录。为了不搞砸事情,我考虑在每行命令结尾添加当前目录作为注释。以下是一个例子:
$ cd /usr/local/wherever
$ grep timmy accounts.txt
我希望bash能够保存最后一条命令为:
grep timmy accounts.txt # /usr/local/wherever
这个想法是这样的,我可以立即看到我发出命令的位置。
这是原始的一行版本。我也发布了短函数版本和长函数版本,并添加了几个功能。我喜欢函数版本,因为它们不会干扰环境中的其他变量,并且比一行代码更易读。这篇文章介绍了它们的工作原理,这些信息可能在其他文章中没有重复。
~/.bashrc
文件中:export PROMPT_COMMAND='hpwd=$(history 1); hpwd="${hpwd# *[0-9]* }"; if [[ ${hpwd%% *} == "cd" ]]; then cwd=$OLDPWD; else cwd=$PWD; fi; hpwd="${hpwd% ### *} ### $cwd"; history -s "$hpwd"'
rm subdir/file ### /some/dir
###
作为注释定界符,以使其与用户可能输入的注释区别开来,并减少在空白命令行上按下回车时会累积的旧路径注释被剥离的机会。不幸的是,这样做的副作用是一个命令像echo " ### "
就会变形,尽管这应该相当罕见。HISTTIMEFORMAT
或以其他方式修改历史记录。可以在注释中添加date
命令代替HISTTIMEFORMAT
功能。然而,如果由于某种原因您需要使用它,它仍然可以在子shell中正常工作,因为它会自动取消设置:$ htf="%Y-%m-%d %R " # save it for re-use
$ (HISTTIMEFORMAT=$htf; history 20)|grep 11:25
这里有一些非常小的问题。其中之一是如果您像这样使用history
命令:
$ history 3
echo "hello world" ### /home/dennis
ls -l /tmp/file ### /home/dennis
history 3
history
命令本身上显示注释,尽管如果按上箭头或发出另一个 history
命令,您会看到它。PS1
主提示符时,Bash 执行包含在 PROMPT_COMMAND
变量中的命令。 这个小脚本利用这一点来获取历史记录中的最后一个命令,添加注释并保存回去。hpwd=$(history 1) # grab the most recent command
hpwd="${hpwd# *[0-9]* }" # strip off the history line number
if [[ ${hpwd%% *} == "cd" ]] # if it's a cd command, we want the old directory
then # so the comment matches other commands "where *were* you when this was done?"
cwd=$OLDPWD
else
cwd=$PWD
fi
hpwd="${hpwd% ### *} ### $cwd" # strip off the old ### comment if there was one so they
# don't accumulate, then build the comment
history -s "$hpwd" # replace the most recent command with itself plus the comment
这是一个长函数版本。虽然它有些臃肿,但它添加了几个有用的功能。我也发布了一个一行代码的版本和一个更短的函数版本。我喜欢函数版本,因为它们不会覆盖您环境中的其他变量,并且比一行代码更易读。请阅读下面的函数入口和注释以获取有关其工作方式和一些限制的其他信息。为了保持组织的整洁性,我在每个版本中都发布了一个答案。
要使用此函数,请将其保存在名为hcmnt
的文件中,位于像/usr/local/bin
这样的位置(如果需要,可以使用chmod +x
命令)。然后在您的~/.bashrc
中按如下方式调用:
source /usr/local/bin/hcmnt
export hcmntextra='date "+%Y%m%d %R"'
export PROMPT_COMMAND='hcmnt'
PROMPT_COMMAND
或 hcmntextra
的函数文件。将它们保留为默认值并在你的 .bashrc
中按上述方法包含它们,并在那里编辑以设置 hcmnt
的选项或更改或取消设置 hcmntextra
。与短函数不同,使用此函数必须同时设置 hcmntextra
变量并使用 -e
选项才能使该功能生效。-l
filename 选项,如下所示:export PROMPT_COMMAND="hcmnt -l ~/histlog"
您可以使用任何选项的组合,但-n
和-t
是互斥的。
#!/bin/bash
hcmnt() {
# adds comments to bash history entries (or logs them)
# by Dennis Williamson - 2009-06-05 - updated 2009-06-19
# https://dev59.com/_HNA5IYBdhLWcg3wfNyO
# (thanks to Lajos Nagy for the idea)
# the comments can include the directory
# that was current when the command was issued
# plus optionally, the date or other information
# set the bash variable PROMPT_COMMAND to the name
# of this function and include these options:
# -e - add the output of an extra command contained in the hcmntextra variable
# -i - add ip address of terminal that you are logged in *from*
# if you're using screen, the screen number is shown
# if you're directly logged in, the tty number or X display number is shown
# -l - log the entry rather than replacing it in the history
# -n - don't add the directory
# -t - add the from and to directories for cd commands
# -y - add the terminal device (tty)
# text or a variable
# Example result for PROMPT_COMMAND='hcmnt -et $LOGNAME'
# when hcmntextra='date "+%Y%m%d %R"'
# cd /usr/bin ### mike 20090605 14:34 /home/mike -> /usr/bin
# Example for PROMPT_COMMAND='hcmnt'
# cd /usr/bin ### /home/mike
# Example for detailed logging:
# when hcmntextra='date "+%Y%m%d %R"'
# and PROMPT_COMMAND='hcmnt -eityl ~/.hcmnt.log $LOGNAME@$HOSTNAME'
# $ tail -1 ~/.hcmnt.log
# cd /var/log ### dave@hammerhead /dev/pts/3 192.168.1.1 20090617 16:12 /etc -> /var/log
# INSTALLATION: source this file in your .bashrc
# will not work if HISTTIMEFORMAT is used - use hcmntextra instead
export HISTTIMEFORMAT=
# HISTTIMEFORMAT still works in a subshell, however, since it gets unset automatically:
# $ htf="%Y-%m-%d %R " # save it for re-use
# $ (HISTTIMEFORMAT=$htf; history 20)|grep 11:25
local script=$FUNCNAME
local hcmnt=
local cwd=
local extra=
local text=
local logfile=
local options=":eil:nty"
local option=
OPTIND=1
local usage="Usage: $script [-e] [-i] [-l logfile] [-n|-t] [-y] [text]"
local newline=$'\n' # used in workaround for bash history newline bug
local histline= # used in workaround for bash history newline bug
local ExtraOpt=
local LogOpt=
local NoneOpt=
local ToOpt=
local tty=
local ip=
# *** process options to set flags ***
while getopts $options option
do
case $option in
e ) ExtraOpt=1;; # include hcmntextra
i ) ip="$(who --ips -m)" # include the terminal's ip address
ip=($ip)
ip="${ip[4]}"
if [[ -z $ip ]]
then
ip=$(tty)
fi;;
l ) LogOpt=1 # log the entry
logfile=$OPTARG;;
n ) if [[ $ToOpt ]]
then
echo "$script: can't include both -n and -t."
echo $usage
return 1
else
NoneOpt=1 # don't include path
fi;;
t ) if [[ $NoneOpt ]]
then
echo "$script: can't include both -n and -t."
echo $usage
return 1
else
ToOpt=1 # cd shows "from -> to"
fi;;
y ) tty=$(tty);;
: ) echo "$script: missing filename: -$OPTARG."
echo $usage
return 1;;
* ) echo "$script: invalid option: -$OPTARG."
echo $usage
return 1;;
esac
done
text=($@) # arguments after the options are saved to add to the comment
text="${text[*]:$OPTIND - 1:${#text[*]}}"
# *** process the history entry ***
hcmnt=$(history 1) # grab the most recent command
# save history line number for workaround for bash history newline bug
histline="${hcmnt% *}"
hcmnt="${hcmnt# *[0-9]* }" # strip off the history line number
if [[ -z $NoneOpt ]] # are we adding the directory?
then
if [[ ${hcmnt%% *} == "cd" ]] # if it's a cd command, we want the old directory
then # so the comment matches other commands "where *were* you when this was done?"
if [[ $ToOpt ]]
then
cwd="$OLDPWD -> $PWD" # show "from -> to" for cd
else
cwd=$OLDPWD # just show "from"
fi
else
cwd=$PWD # it's not a cd, so just show where we are
fi
fi
if [[ $ExtraOpt && $hcmntextra ]] # do we want a little something extra?
then
extra=$(eval "$hcmntextra")
fi
# strip off the old ### comment if there was one so they don't accumulate
# then build the string (if text or extra aren't empty, add them plus a space)
hcmnt="${hcmnt% ### *} ### ${text:+$text }${tty:+$tty }${ip:+$ip }${extra:+$extra }$cwd"
if [[ $LogOpt ]]
then
# save the entry in a logfile
echo "$hcmnt" >> $logfile || echo "$script: file error." ; return 1
else
# workaround for bash history newline bug
if [[ $hcmnt != ${hcmnt/$newline/} ]] # if there a newline in the command
then
history -d $histline # then delete the current command so it's not duplicated
fi
# replace the history entry
history -s "$hcmnt"
fi
} # END FUNCTION hcmnt
# set a default (must use -e option to include it)
export hcmntextra='date "+%Y%m%d %R"' # you must be really careful to get the quoting right
# start using it
export PROMPT_COMMAND='hcmnt'
更新于2009-06-19:添加了有助于记录日志的选项(IP和TTY),解决了重复条目问题的解决方法,删除了多余的空赋值。
trap 'hcmnt' DEBUG
而不是PROMPT_COMMAND='hcmnt'
,可以避免在 shell 被终止时出现历史记录丢失的情况。 - simonmysunbash
或 zsh
中使用的历史记录写入 sqlite 数据库。记录内容包括当前工作目录、命令退出码、命令开始和结束时间、会话开始和结束时间、tty 等等。ash_query
工具中保存并提供它们。这里有一些有用的预设查询,但由于我很熟悉 SQL,所以通常只是打开数据库并交互式地查询需要查找的内容。vagrant@precise32:~$ ash_query -q CWD
session
when what
1
2014-08-27 17:13:07 ls -la
2014-08-27 17:13:09 cd .ash
2014-08-27 17:16:27 ls
2014-08-27 17:16:33 rm -rf advanced-shell-history/
2014-08-27 17:16:35 ls
2014-08-27 17:16:37 less postinstall.sh
2014-08-27 17:16:57 sudo reboot -n
使用当前工作目录及其子目录的相同历史记录:
vagrant@precise32:~$ ash_query -q RCWD
session
where
when what
1
/home/vagrant/advanced-shell-history
2014-08-27 17:11:34 nano ~/.bashrc
2014-08-27 17:12:54 source /usr/lib/advanced_shell_history/bash
2014-08-27 17:12:57 source /usr/lib/advanced_shell_history/bash
2014-08-27 17:13:05 cd
/home/vagrant
2014-08-27 17:13:07 ls -la
2014-08-27 17:13:09 cd .ash
/home/vagrant/.ash
2014-08-27 17:13:10 ls
2014-08-27 17:13:11 ls -l
2014-08-27 17:13:16 sqlite3 history.db
2014-08-27 17:13:43 ash_query
2014-08-27 17:13:50 ash_query -Q
2014-08-27 17:13:56 ash_query -q DEMO
2014-08-27 17:14:39 ash_query -q ME
2014-08-27 17:16:26 cd
/home/vagrant
2014-08-27 17:16:27 ls
2014-08-27 17:16:33 rm -rf advanced-shell-history/
2014-08-27 17:16:35 ls
2014-08-27 17:16:37 less postinstall.sh
2014-08-27 17:16:57 sudo reboot -n
FWIW(For What It's Worth)- 我是该项目的作者和维护者。
这是一个以函数形式呈现的短版本。我还发布了一个一行代码的原版和一个带有多个附加功能的长函数。我喜欢函数版本,因为它们不会覆盖您环境中的其他变量,并且比一行代码更易读。请阅读一行代码的条目以获取有关如何使用以及一些限制的其他信息。为了保持组织更加有序,我已经将每个版本都发布在自己的答案中。
要使用此版本,请将其保存到名为 hcmnts
的文件中,放在类似 /usr/local/bin
的位置(如果需要,可以使用 chmod + x
进行授权),然后在您的 ~/.bashrc
中像这样引用:
source /usr/local/bin/hcmnts
如果您不想要日期和时间,可以将设置hcmntextra
的行注释掉(或更改其格式或使用其他命令而非date
)。
就是这样简单。
#!/bin/bash
hcmnts() {
# adds comments to bash history entries
# the *S*hort version of hcmnt (which has many more features)
# by Dennis Williamson
# https://dev59.com/_HNA5IYBdhLWcg3wfNyO
# (thanks to Lajos Nagy for the idea)
# INSTALLATION: source this file in your .bashrc
# will not work if HISTTIMEFORMAT is used - use hcmntextra instead
export HISTTIMEFORMAT=
# HISTTIMEFORMAT still works in a subshell, however, since it gets unset automatically:
# $ htf="%Y-%m-%d %R " # save it for re-use
# $ (HISTTIMEFORMAT=$htf; history 20)|grep 11:25
local hcmnt
local cwd
local extra
hcmnt=$(history 1)
hcmnt="${hcmnt# *[0-9]* }"
if [[ ${hcmnt%% *} == "cd" ]]
then
cwd=$OLDPWD
else
cwd=$PWD
fi
extra=$(eval "$hcmntextra")
hcmnt="${hcmnt% ### *}"
hcmnt="$hcmnt ### ${extra:+$extra }$cwd"
history -s "$hcmnt"
}
export hcmntextra='date +"%Y%m%d %R"'
export PROMPT_COMMAND='hcmnts'
以下是我使用的一行代码。将其放在这里是因为它要简单得多,我对每个会话的历史记录没有任何问题,我只是希望有一个与工作目录相关的历史记录。
另外,上面那个一行代码也会对您的用户界面产生影响太大。
export PROMPT_COMMAND='if [ "$(id -u)" -ne 0 ]; then echo "$(date "+%Y-%m-%d.%H:%M:%S") $(pwd) $(history 1)" >> ~/.bash.log; fi'
由于我的主目录通常是一个跨挂载的 Gluster 文件系统,这会导致它成为我所做的一切操作的历史记录。根据您的工作环境,可以选择将 $(hostname)
添加到上面的 echo 命令中...
即使有 10 万个条目,grep 也足够好用了。无需使用 sqlite 记录日志。只需不要在命令行上输入密码即可。密码已经过时了!
此外,对于搜索,我倾向于执行以下操作:
function hh() {
grep "$1" ~/.bash.log
}
shournal --query -cmdcwd "$PWD"
shournal --query -cmdcwd -like "$PWD/%"
$(jq -n --arg command "$1" --arg path "$PWD" '{"command":$command, "path":$path}' | "$(echo 'readlink -f $(which nc)' | nix run nixpkgs.netcat)" -N -U ~/.config/moscoviumOrange/monitor.soc &)
history
的结果中得到了两行,一行没有注释,另一行有注释。 - Gauthierhistory -s
之前包含一个history -d $(hpwd% *)
来解决它。类似这样:PROMPT_COMMAND='hpwd=$(history 1); history -d ${hcmnt% *}; hpwd="${hpwd# *[0-9]* }"; if [[ ${hpwd%% *} == "cd" ]]; then cwd=$OLDPWD; else cwd=$PWD; fi; hpwd="${hpwd% ### *} ### $cwd"; history -s "$hpwd"'
- humanityANDpeace