如何覆盖 pushd 和 popd 对 dirs 的自动调用?

How to override pushd's and popd's automatic call to dirs?

我的bash (4.1) 目录堆栈通常有十几个或更多条目。我想用 dirs -v 替换 dirs 的输出,这样我就再也不用用 pushd 玩 "guess the magic number" 了。

我的部分解决方案

我将 dirs 替换为使用 command:

执行 dirs -v 的函数
dirs()
{
    # "command" builtin prevents infinite recusion by suppressing lookup
    # of function and alias names.
    command dirs -v "${@}"
}

(更新:在pneumatics' suggestion, I now use builtin而不是command。它没有解决这个问题,但它稍微安全一些。)

dirs 输出现在是可读的,但是 pushdpopd 仍然产生旧的斜线呕吐物:

$ pushd ~/just/one/more/and/ill/quit/i/promise
~/just/one/more/and/ill/quit/i/promise ~/tmp/bash/bash-4.1...
may/give/rise/to/dom /oh/no/not/again /var/adm/log /omg/wt...
va/lang ~/doc/comp/java/api/java/util/regex barf barf barf...

用别名替换我的 dirs 函数后,我得到了相同(缺少)的结果:

alias dirs='command dirs -v "${@}"'

解决方法

我最终通过覆盖 pushdpopd 得到了我想要的输出:

pushd()
{
    command pushd "${@}" >/dev/null &&
      dirs
}
# popd is similar.

这可行,但它需要覆盖 三个 内置函数而不是一个。此外,这可能是 pushdpopd 在某些我没有想到的极端情况下的不完美模仿。我宁愿只覆盖 dirs

我的问题

为什么覆盖 dirs 不起作用? 根据 bashman 页面,pushdpopd:

If the pushd command is successful, a dirs is performed as well.

那么为什么 pushdpopd 似乎调用内置函数 dirs 而不是函数或别名?

关于 bash 文档的脚注

"a dirs is performed as well" 的段落在 bashref.* online manual 中缺失,但出现在 manbash.*builtins.* 中。我的 bash 4.1 文档和当前的 4.4 文档以相同的方式不一致,这表明 4.4(如果我有的话)会表现相同。

您想使用 builtin 而不是 command。我认为这就是您所追求的,覆盖 pushdpopd,同时保持 dirs 不变。

pushd(){ builtin pushd "$@" >/dev/null && dirs -v; }
popd() { builtin popd "$@" >/dev/null  && dirs -v; }

根据我对 bash source code, both pushd and popd call the dirs 内置函数(没有任何参数)的阅读,而不是咨询定义的别名和函数。

虽然内置函数很少利用外部功能,但当内置函数确实需要外部功能时,调用另一个内置函数似乎是先例。例如,当 pushd 更改目录时,it calls the cd builtin. Or when declare needs to type cast a variable, it calls the set builtin.

如何解决这个问题?嗯,可以从源代码重新编译 bash:让 pushdpopd 传递 -v 参数是微不足道的。还可以向 bash 上游请求添加一个环境变量,该变量在用作 pushd/popd 的助手时定义了默认的 dirs 选项。但似乎最快的用户空间解决方案就像您所做的那样:只需覆盖所有三个。