在Git中,是否可以在shell的内置命令中使用别名扩展?

3

我在.gitconfig文件中有以下别名:

freq = !history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n 5

这个命令应该显示我使用最多的5个git命令。取自于这里:http://alblue.bandlem.com/2011/04/git-tip-of-week-aliases.html(虽然我知道这篇文章已经老了)

当我运行这个命令时,它不会输出任何内容:

$ git freq
$

但是,如果我运行history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n 5,输出如下:

$ history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n
   5 git log
   5 git status
   5 git current
   5 git co master
   4 git co other-branch

我猜这是因为history是一个shell内置命令,如果我改变freq别名,比如:

freq = !history

I get:

$ git freq
error: cannot run history: No such file or directory
fatal: While expanding alias 'freq': 'history': No such file or directory
$

有没有办法让它工作?
1个回答

2

原因

阅读问题后,我认为问题出在 history 是一个shell内置命令。

Git调试

通过将 GIT_TRACE 环境变量设置为 true,可以调试Git命令(有关更多信息,请参见Scott Chacon的Git书中有关Git内部 - 环境变量的相关章节)。

$ 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'

查看源代码 可以发现外部命令是通过 execv_shell_cmd 函数处理的,该函数调用 sane_execvp,这是一个更加用户友好的前端,用于调用 execvp 标准库函数。如果命令名称不包含斜杠,则在 PATH 中的每个目录中搜索与命令同名的可执行文件。由于 history 是 shell 内置命令,因此永远找不到它并返回错误。请参见:


我不会放弃,后来我进行了进一步实验,使用其他 shell 内置命令进行了测试,那些实验都成功了,所以我认为问题肯定出在别处:

非交互式 shell 中没有历史记录

阅读 Bash 手册可以得知,Bash 历史记录功能 仅在交互式 shell 中使用,而由 exec 调用启动的 shell 则是非交互式的。这就是为什么运行 history 内置命令不会输出任何内容。


解决方案

选项 1:在非交互式 shell 中打开历史记录功能

感谢 Unix 和 Linux 上的 Gilles,发现可以通过设置 HISTFILE shell 变量并运行 set -o history 来打开非交互式 shell 中的历史记录功能,如下所示:

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 命令。
第三种选择是使用 Bash 别名,而不是使用 Git 别名:
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"

.bash_history中读取似乎与我的情况下使用history给出的结果不同。当别名设置为你的解决方法时,我没有得到任何输出(我已经检查了我的~/.bash_history,实际上没有git命令)。但是,如果我运行$ history,我可以看到我之前发出的一些git命令...那么也许我可以使用像!bash -c "history"这样的东西? - tonix
我尝试过:freq = !bash -c“history | cut -c 8- | grep ^ git | sort | uniq -c | sort -n -r | head -n 5”,但似乎不起作用... - tonix
@tonix。没错,这可能是您想要的行为,也可能不是。 - Anthony Geoghegan
@tonix,自昨天以来,我已经多次重新编辑了我的答案,并建议您重新阅读它,因为我发现了您问题的真正原因 - 以及解决方案。 - Anthony Geoghegan
1
是的,很抱歉我一直没出现,有些事情需要处理。感谢您的帮助,我现在会使用“别名”! - tonix
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接