My bash
(4.1) directory stack usually has a dozen or more entries. I wanted to replace the output of dirs
with dirs -v
, so I would never have to play "guess the magic number" with pushd
again.
I replaced dirs
with a function that executed dirs -v
using command
:
dirs()
{
# "command" builtin prevents infinite recusion by suppressing lookup
# of function and alias names.
command dirs -v "${@}"
}
(Update: At pneumatics' suggestion, I now use builtin
instead of command
. It doesn't solve this problem, but it's slightly safer.)
dirs
output was now readable, but pushd
and popd
still produced the old vomit-of-slashes:
$ 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...
I got the same (lack of) result after replacing my dirs
function with an alias:
alias dirs='command dirs -v "${@}"'
I eventually got the output I wanted by overriding pushd
and popd
as well:
pushd()
{
command pushd "${@}" >/dev/null &&
dirs
}
# popd is similar.
This works, but it requires overriding three builtins instead of one. Also, this could be an imperfect impersonation of pushd
and popd
in some corner case I haven't thought of. I'd rather override dirs
only.
Why didn't overriding dirs
work? According to bash
's man
page, under both pushd
and popd
:
If the
pushd
command is successful, adirs
is performed as well.
So why did pushd
and popd
appear to invoke the builtin dirs
instead of the function or the alias?
bash
DocumentationThe paragraphs saying "a dirs
is performed as well" are missing from the bashref.*
online manual, but they appear in the man
pages bash.*
and builtins.*
. Both my bash
4.1 docs and the current 4.4 docs are inconsistent in the same manner, which suggests that 4.4 (if I had it) would behave the same.
From my reading of the bash source code, both pushd
and popd
call the dirs
builtin itself (without any args), rather than consulting defined aliases and functions.
While it seems rare that a builtin leverages external functionality, when a builtin does need external functionality, calling another builtin seems precedent. For example, when pushd
changes directory, it calls the cd
builtin. Or when declare
needs to type cast a variable, it calls the set
builtin.
How to work around this? Well, one could recompile bash from source: it'd be trivial to have pushd
and popd
pass the -v
argument. One could also petition bash upstream to add an environment variable that defined the default dirs
options when used as a helper to pushd
/popd
. But it seems like the quickest userland solution is as you've done: just override all three.
You want to use builtin
instead of command
. I think this is what you're after, overrides to pushd
and popd
, while leaving dirs
as-is.
pushd(){ builtin pushd "$@" >/dev/null && dirs -v; }
popd() { builtin popd "$@" >/dev/null && dirs -v; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With