原因
看了这个问题后,我也认为问题是由于history是一个shell内置的。
调试 git
可以通过设置GIT_TRACE环境变量来调试Git命令
到true(有关更多信息,请参阅 Scott Chacon 的 Git Book 中Git Internals - Environment Variables 的相关部分)。
$ GIT_TRACE=true git freq
10:14:11.901296 git.c:554 trace: exec: 'git-freq'
10:14:11.901296 run-command.c:341 trace: run_command: 'git-freq'
10:14:11.932496 run-command.c:341 trace: run_command: 'history | tail'
10:14:11.963697 run-command.c:192 trace: exec: '/bin/sh' '-c' 'history | tail' 'history | tail'
Checking the source 表明外部命令由调用sane_execvp 的execv_shell_cmd 函数处理,这是execvp 的更用户友好的前端标准库 函数——如果命令名称不包含斜线 - 在 PATH 中的每个目录中搜索与该命令同名的可执行文件。由于history 是一个内置的shell,它永远不会被发现,所以它返回一个错误。见
我不是一个放手的人,后来我使用其他 shell 内置函数进行了进一步的实验,这些实验成功了,所以我认为它一定是别的东西:
非交互式 shell 没有历史记录
阅读 Bash 手册说明 Bash History Facilities 仅用于 interactive shells 而由 exec 调用启动的 shell 是非交互式的。这就是为什么运行 history 内置函数不会打印任何输出的原因。
解决方案
选项 1:在非交互式 shell 中开启历史记录功能
感谢Gilles on Unix and Linux,原来非交互式shell中的历史功能可以通过设置HISTFILE shell变量然后运行set -o history来开启,运行可以看到:
bash -c "HISTFILE=~/.bash_history; set -o history; history"
在.gitconfig 中设置以下别名将起作用:
freq = "!HISTFILE=$HOME/.bash_history; set -o history; history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n 5"
注意:如果您使用 Bash 以外的 shell,则需要指定 shell 将其命令历史记录保存到的文件的名称。如果您使用的是 Bash,HISTFILE shell 变量可能设置为 $HOME/.bash_history 以外的其他值。
另外,HISTFILE 等 shell 变量不应导出为环境变量(git 可用),这就是我在这里没有使用它的原因。
选项 2:从历史文件中读取
更简单的解决方案是直接从 shell 历史文件中读取:
freq = !grep ^git $HOME/.bash_history | sort | uniq -c | sort -n -r | head -n 5
这将得到与在新的交互式 shell 中运行 history 命令相同的结果,因为当新的 shell 启动时,除了从历史文件中读取的内容之外,它的历史记录中没有任何命令。
另外,直接从历史文件读取时,不需要cut 命令。
选项 3:Bash 别名
另一种选择是忘记使用 Git 别名,而直接使用 Bash 别名:
alias git-freq='history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n
使 shell 历史文件保持最新
我想我会添加以下内容作为有用的辅助信息。请注意,它适用于交互式 Bash shell,它可能适用于也可能不适用于其他现代 shell。
我经常在同一主机上同时运行多个 shell,我不希望在退出 shell 时覆盖保存的历史命令。这是我的.bashrc 设置,可确保在每个命令之后写入 shell 历史文件:
# Append history entries on logout - instead of over-writing the history file.
shopt -s histappend
# Don't store duplicate lines in the history.
# Ignore any commands that begin with a space.
HISTCONTROL=ignoredups:ignorespace
# Use a very large history file to store lots of commands.
# See http://mywiki.wooledge.org/BashFAQ/088
HISTFILESIZE=5000
# Keeping a large history requires system resources
HISTSIZE=3000
# Append to the history file after every command.
PROMPT_COMMAND="history -a"