27

Just about every time I 'cd' to a different directory on my machine (in this case, running Mac OS X 10.6.7) via the command line (which is bash), I immediately type 'ls' to get the list of contents in that directory. I'm trying to figure out a way to override 'cd' so that it changes to the requested directory and then gives me the list in one shot.

I've been able to get the basic functionality I'm looking for working by with the following line added to my ~/.bash_profile

function cl() { cd "$@"; ls -l; }

This works as expected. Changing to the requested directory and then showing me the contents. Where I run into an issue is trying to override "cd" itself instead of creating a new "cl" command.

The following things do not work

##### Attempt 1 #####
# Hangs the command line

function cd() { cd "$@"; ls -l; }


##### Attempt 2 #####
# Hangs the command line

function cd() { 'cd' "$@"; ls -l; }


##### Attempt 3 #####
# Does not change directory. 
# Does list contents, but of the directory where you started.

function cd() { /usr/bin/cd "$@"; ls -l; }


#### Other attempts that fail in various ways #####
alias cd=cd "$@"; ls -la;
alias cd="cd '$@'; ls -la;"
alias cd='cd "$@"'; ls -la;
alias cd=/usr/bin/cd "$@"; ls -la;

I also tried several other iterations that aren't listed as well as making an alias that points to the working 'cl' function. None of which worked.

What I've read in documentation talks about the fact that 'cd' can't be run as an external command (which is what I understand to be the way the function would need to use it).

So, I can currently use my "cl" command and get what I want, but the question is/remains:

Is there a way to override the behavior of 'cd' to have it change to the requested directory and then do something else afterward?

3 Answers 3

37

The following should work:

function cd() { builtin cd "$@" && ls -l; }

Since the function is on a single line, ensure that it is terminated with ; as above in order to work correctly.

8
  • Doesn't work for a parameterless call.
    – Daniel Beck
    Commented Jun 13, 2011 at 16:04
  • 3
    @DanielBeck - at least on my Mac OSX (10.6.7) it this is working fine with a parameterless call. The behavior is that it changes to the home directory and then does the list of that dir. Effectively duplicating the functionality of plain-old 'cd' with the new feature added at the end. Commented Jun 13, 2011 at 17:04
  • 2
    @Alan True, I tested that by writing that code ad-hoc into the shell. It seems the behavior of cd "" (to what this answer should evaluate is different from cd "$@" with empty $@. Sorry about the confusion.
    – Daniel Beck
    Commented Jun 13, 2011 at 17:08
  • @DanielBeck - no worries. Commented Jun 13, 2011 at 17:15
  • 2
    Note: I'm changing this to the accepted answer since it is a precise answer to the question. The one by @RichHomolka is great for addressing the principal, but this one is exactly what can be put in your ~/.bash_profile file. Commented Jun 13, 2011 at 17:17
12

I think you're running into a loop. Your cd function is calling cd, which is... the function.

You need to know about builtin which is a keyword which makes command lookup use the the Bash builtins like cd and not your function

function cd
{
    builtin cd "$1"
    : do something else
}

Also, calling /usr/bin/cd will never work, even if such a command existed.

What would happen:

  • My Bash shell is in dir /bash/dir.
  • I run a command /usr/bin/cd /dir/for/cd.
  • /usr/bin/cd goes to dir /dir/for/cd.
  • /usr/bin/cd exits.
  • Bash is still in /bash/dir, because the child process /usr/bin/cd can not affect the parent.

Also aliases are simple text substitutions. They can never have parameters.

5
  • I suggest builtin cd $@ && ls; since the cd call should succeed for everything else to make sense. Also, remember special cases such as cd (no argument).
    – Daniel Beck
    Commented Jun 13, 2011 at 15:58
  • Side Note: I tried to mark this as the accepted answer, but it's telling me I have to wait another 6 min before I can do that. Didn't know there was a built in delay before you could do that. Commented Jun 13, 2011 at 16:03
  • @Alan Another, better solution might come up in a very reasonable timeframe (i.e. those few minutes you have to wait). I guess that's why there's a system mandated delay: You should prefer quality over speed. It shouldn't be too much of an inconvenience.
    – Daniel Beck
    Commented Jun 13, 2011 at 16:08
  • And we have a winner. This works like a champ. Per @DanielBeck, I changed to use && and condensed it to one line with: function cd { builtin cd "$@" && ls -l; } Commented Jun 13, 2011 at 16:42
  • @DanielBeck - Yeah, I wan't worried about it. Just never noticed it before. I can see the logic in it, just surprised me a little. Commented Jun 13, 2011 at 16:46
6

I suggest not to override the cd because there are some other scripts hijacking the 'cd' function, for example, rvm. It would be better to choose another name, like 'd', instead and don't specify the 'builtin' type in your function; otherwise, the hijacker would not function. I am using the code below:

function d() { cd "$@" && ls;} 
1
  • I thought this wouldn't work though, per the OP's question.
    – papiro
    Commented May 18, 2016 at 20:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .