68

I find that I often do the following:

%> cd bla/bla
%> ls

I would like it that whenever I cd into a directory it automatically does an ls.

I fiddled with my .bashrc for a while, but couldn't figure out how to make it happen.

Kevdog777
  • 3,224
RobKohr
  • 789
  • 1
  • 6
  • 4

13 Answers13

69

You can do this with a function:

$ cdls() { cd "$@" && ls; }

The && means 'cd to a directory, and if successful (e.g. the directory exists), run ls'. Using the && operator is better than using a semicolon ; operator in between the two commands, as with { cd "$@"; ls; }. This second command will run ls regardless if the cd worked or not. If the cd failed, ls will print the contents of your current directory, which will be confusing for the user. As a best practice, use && and not ;.

$ cdls /var/log
CDIS.custom     fsck_hfs.log    monthly.out     system.log
$ pwd
/var/log

In general, it is a bad practice to rename a command which already exists, especially for a commonly called command like cd. Instead, create a new command with a different name. If you overwrite cd with a function or alias which is also named cd, what would happen when you enter a directory with 100,000 files? There are many utilities that use cd, and they may get confused by this unusual behavior. If you use a shared account (Such as root when you are working with other system administrators), it can be very dangerous to replace an existing command because the environment is different from what people expect.

Stefan Lasiewski
  • 19,754
  • 24
  • 70
  • 85
  • That command really change directory? From bash's man page: "There is no mechanism for using arguments in the replacement text. If arguments are needed, a shell function should be used" – enzotib Sep 09 '11 at 18:58
  • @enzotib : Yes, this really does change directory, at least for me. I updated my answer to show the output of pwd. Not sure if this is a best practice, but it is commonly done. See http://tldp.org/LDP/abs/html/aliases.html for some examples. – Stefan Lasiewski Sep 09 '11 at 19:05
  • 2
    First: it does not work here. Second: in that page they use variables, not positional parameters. Third: ABS is a common source of bad practices. – enzotib Sep 09 '11 at 19:07
  • Ok fine, I added a function also. Maybe ABS is full of bad practices (Some people say this about shell scripting, in general), but at least they are advanced bad practices. – Stefan Lasiewski Sep 09 '11 at 19:10
  • I would remove the downvote, but cannot if I do not understand why it does not work here. – enzotib Sep 09 '11 at 19:13
  • The alias won't work. Aliases don't take arguments, the $1 will remain in the alias expansion. – Gilles 'SO- stop being evil' Sep 09 '11 at 23:38
  • 1
    The alias works for me on Snow Leopard but not on CentOS5 or CentOS6. I updated my answer to use a function only. No aliases. – Stefan Lasiewski Sep 10 '11 at 00:37
  • I have something similar, but I call it cdl, since the point is to save typing. – Keith Thompson Jul 01 '13 at 19:55
  • 1
    Does not work for me. Tried @frabjous answer and it works: cd() { builtin cd "$@" && pwd; }; -- using standard bourne shell on macOS (Sierra 10.12.6) – Swivel Sep 30 '17 at 19:10
  • This doesn't work. – temporary_user_name Apr 26 '19 at 19:50
  • 1
    i mean i'd rather have it override cd for the 100,000 different times i have to use it, rather than the one folder I could potentially come across with 100,000 files. Having to type cdls defeats the point and might as well just do the two commands but that's just me – Emobe Sep 17 '19 at 11:00
  • Doesn't work. Current working directory remains unchanged!!! – Aleksandr Hovhannisyan Oct 20 '19 at 13:03
  • What does the @ mean here? I haven't seen that syntax for an argument before. – Hashim Aziz Apr 11 '20 at 02:04
  • How to undo this? – Kangarooo Mar 14 '21 at 18:26
  • I followed this answer but just named the function c. That doesn't defeat the point anymore - of having to type ls anyway. – Daniel Katz Aug 26 '21 at 11:50
57

I have this in my .bashrc, and it works fine.

function cd {
    builtin cd "$@" && ls -F
    }

Earlier in my .bashrc I have: [ -z "$PS1" ] && return, and everything after that line only applies to interactive sessions, so this doesn't affect how cd behaves in scripts.

frabjous
  • 8,691
  • 1
    What exactly does [ -z "$PS1" ] && return do? – syntagma Jan 21 '15 at 14:34
  • 3
    [ -z "$PS1" ] checks if the $PS (interactive prompt variable) is "zero length" (-z). If it is zero length, this means it has not been set, so Bash must not be running in interactive mode. The && return part exits from sourcing .bashrc at this point, under these conditions. – frabjous Jan 21 '15 at 17:42
  • 3
    Another way to check for interactivity is to look for i in "$-": case "$-" in *i*) ;; *) return ;; esac. – Kusalananda Jan 09 '17 at 19:38
  • @Kusalananda & frabjous: Are there cases where one should be used instead of the other? – Swivel Sep 30 '17 at 19:14
  • 3
    @Swivel The PS1 variable may be unset or empty and the shell may still be interactive (but without a prompt). I would check $- to make sure. – Kusalananda Sep 30 '17 at 19:24
  • @Kusalananda That lines up perfectly with what I was finding as well. I'll check $-. Thanks a lot :D – Swivel Sep 30 '17 at 19:38
  • @Kusalananda I actually realized that when I was editing my .bashrc and noticed the custom prompt I wrote. Completely invalidates [ -z "$PS1" ]. For those who also export their own custom prompts, remember this! If you must use $PS1 for checking for interactive shell, remember to place your export PS1 after the check! – Swivel Sep 30 '17 at 19:40
  • 1
    @Swivel There shouldn't be a reason to export PS1. – Kusalananda Sep 30 '17 at 19:48
  • Where is .bashrc?! – Black Jun 25 '18 at 14:19
  • @Black Typically your .bashrc would be in your home folder. ~/.bashrc. It'll be hidden since it starts with a period. If it doesn't exist, you can create it. That's a pretty elementary point, so you might want to read a basic bash tutorial before implementing suggestions like these. – frabjous Jun 26 '18 at 14:44
  • @frabjous, If I would just had the time to do so... I made it work nevertheless by just reading this answer which explains it much much better and beginner friendly: https://unix.stackexchange.com/a/451781/124191 – Black Jun 26 '18 at 15:05
  • What does the @ mean here? I haven't seen that syntax for an argument before. – Hashim Aziz Apr 11 '20 at 02:05
  • A little variation to show only as much file as would fit the screen.
    builtin cd "$@" && ls -la | head -n $(tput lines)
    
    – Slawa Jul 03 '20 at 10:23
  • What is -F for – ajinzrathod Jun 20 '21 at 06:30
  • this works for me also in zsh – Filip Górny Aug 25 '21 at 14:24
21

off-topic, since the question is tagged /bash, but as some questions are closed as duplicate of this one that don't mention bash:

With zsh:

chpwd() ls

The chpwd() function is called by zsh whenever the current directory changes (by way of cd, pushd, popd... see also the autocd feature). tcsh has a similar feature and is probably where zsh got it from.

In newer versions of zsh, like for other hook functions, you can do:

my_chpwd_hook() ls
chpwd_functions+=( my_chpwd_hook )

That approach would be preferable if you or some third-party plugin you use have separate and independent things to do when the current working directory changes.

4

The common solution of creating alias for cd command is not perfect because there are other commands which can change your current directory like popd or even running a script with cd command in it.

It is better to use $PROMPT_COMMAND Bash hook which executes a command before returning a prompt.

The command (a function in our case) will execute ls only if directory has changed to reduce screen noise. Code for .bashrc:

    #each console has its own file to save PWD
    PrevDir=$(tty) 
    PrevDir=/tmp/prev-dir${PrevDir////-}
    #don't ls when shell launched
    echo $PWD > $PrevDir
    LsAfterCd() {
        [[ "$(< $PrevDir)" == "$PWD" ]] && return 0

        ll --color=always | sed 1d

        echo $PWD > $PrevDir
    }
    PROMPT_COMMAND=LsAfterCd
Jack
  • 311
2

Why not add an alias to your .bashrc file?

Something like:

alias cdls='cd "$@" && ls'
don_crissti
  • 82,805
  • @don_crissti A funtion and an alias are different things. So why not? – Jodka Lemon Dec 16 '15 at 14:09
  • This is the quickest and cleanest way of doing what the OP asked for. In my opinion, functions should be used for more complicated things, while making shortcuts for often typed commands are exactly what aliases exist for. – Ghos3t Feb 22 '19 at 01:27
  • 3
    I am having a strange issue with this alias, when I use it like this, cdls projec2, it will show all the files in the project 2 folder but not actually cd to that folder, instead, it will remain in the original folder. – Ghos3t Feb 28 '19 at 23:18
  • Yep, using an alias simply lists contents in that directory, but doesn't actually cd to there. – a3y3 Nov 09 '19 at 16:54
  • 2
    Aliases do not take arguments in $@ like functions do. This alias uses the list of positional parameters from the current shell as the arguments for cd, if there are any (if there are none, it will cd to the user's home directory), and it the executes ls with whatever arguments are following the alias on the command line. – Kusalananda Jan 12 '23 at 18:00
  • 1
    I don't understand all the upvotes for this answer. A simple try-out will show that it doesn't work. (Aliases don't take positional arguments.) – Chris Davies May 20 '23 at 08:32
1

In bash you cannot recur to aliases for action that require parameter. For this there are functions. So put in your ~/.bashrc the following

mycd() {
  cd "$1"
  ls
}
enzotib
  • 51,661
1

An alias would be intuitive, but aliasses might not work as you expect.

An alias like this

alias mycd='cd "$1" && ls'

is effectively

alias mycd='ls'

You cannot put a (positional) parameter "into" an alias definition like "$1" for cd.

Try a command line input

cd "$1"

and see that it exactly does: nothing - because it is just

cd ""

It would be nice if cd did throw an error here complaining about no or illegal arg, but it does just nothing.

An alias is not a function where you can give parameters that are placed by your definition inside the function. With an alias the parameters stay where they are: at the end of your code, so with the initially defined mycd alias the command

mycd bla/bla

would just be a shortcut for

cd "" && ls bla/bla

being effetctively

ls bla/bla

So you indeed need a function like

mycd() { cd "$1" && ls ; }

that you could just type in at command line like an alias definition to become a part of your environment like an alias - with Bash / shell scripting you don't need a script to call a function, just type "mycd xy" at command line.

Dont't forget that in a one-liner function definition you need the semicolon before the "}" and mind the spaces around the curlies), but of course you may define it like

mycd() {
  cd "$1" && ls
}

even at command line if you are more comfort with that (Bash will recognize by "{" that you are not finished and will show the PS2 prompt on return key for further commands before, by "}", your input will be executed, signalized by PS1 prompt after execution).

Of course you know that using parens "()" for function bodies makes them "real" functions without side effects, but here you want the side effect of cd'ing to another dir - a real function would cd inside a subshell, do stuff there, and then return, explicitly without changing caller's environment of which the current directory ("cd") is a property. Note that this has nothing to do with the "alias problem" stated above, although effects might look similar.

As like aliasses you have to put the function definition in one of the initialization scripts ~/.bash_profile, ~/.bash_login, or ~/.profile to provide it for every (sub)shell your are opening.

hh skladby
  • 41
  • 2
  • Note that cd with no argument, is not the same as cd with an empty argument. Neither is "illegal" and both have their uses. Also note that if $1 is set, then the faulty alias would definitely be doing something other than just listing the named directory/file. You can also call you shell function cd if you call cd as command cd inside the function. – Kusalananda Dec 19 '21 at 16:15
  • @they "no arg" or "illegal arg" are meant here as examples for error msgs you may be used to, not as an analysis of command grammar. The naming thing is a special case, anything after a definition may define it new, everyone has to care for themselves. For exposition it's helpful to have separate names. – hh skladby Dec 19 '21 at 16:29
  • @they Would be great if readonly would work on functions – hh skladby Dec 19 '21 at 16:38
  • readonly -f works on functions in bash. See help readonly and the manual. Also, I have not seen standard utilities say things about "no args" or "illegal args". – Kusalananda Dec 19 '21 at 17:25
  • @they Yeh, you're right, I tend to forget the "-f"."no args" or "illegal args" were from their beginnings here written in double quotes with the intention to read them as if they were written in double quotes. I see no "further" way to try to "explain" this. – hh skladby Dec 22 '21 at 18:48
  • @they, I was a little bit tired the hour ago, now let me tell you why I've put also the "-f" option for "readonly" in double quotes: It does not exist in POSIX. As it reads in the specs: "Read-only functions were considered, but they were omitted as not being historical practice or particularly useful. Furthermore, functions must not be read-only across invocations to preclude spoofing (...) of administrative or security-relevant (or security-conscious) shell scripts." The tag here is "Bash", so "-f", OK. – hh skladby Dec 22 '21 at 20:55
0

Place the below code in the .profile and it works. Tested on HP-Unix box.

cdl()
{
if [ "$#" = 0 ]; then
cd ~ && ls -ltr
elif [ -d "$@" ]; then
cd "$@" && ls -ltr
else
echo "$@" directory not found!!!
fi
}

#SET YOUR ALIAS TO CD
alias cd="cdl"
0

Even more handy - with ability to go back in history:

function cd() {
    if [ -d "$@" ]; then
        echo -n "Stack: "
        pushd "$@"
        ls
    else
        builtin cd "$@"
    fi
}
function popd() {
    builtin popd "$@" && ls
}

When you change directory a line with: Stack: (current_dir) (previous_dir) ... will be shown, then ls output. To go back in dirs history just pop this command: popd.

I added else so you'll see an error when trying to go to a wrong directory.

Ctrl-C
  • 241
0

I think it's good to enable ls's options in this way as cd takes no option.

cdls() {
  cd ${$#} && ls ${@:0:$#-1}
}
iBug
  • 3,508
  • 2
    Umm... cd does take options. Additionally, your function can't be used on directories containing spaces, tabs, or newlines in their names. – Kusalananda Jan 11 '19 at 19:20
0

Here's what I find useful (on Debian 9):

c() {
    cd "${@}" \
    && ls --color=always -C \
    | sed '
        # on line 5, print the line,
        5 {
            # append an ellipsis
            a[...]
            # and quit
            q
        }
        # print lines 1-4 verbatim
    '
}

This gives me truncated output with an ellipsis in case there are too many items in that directory so that the console stays clean:

$ c data/git/buildroot/package/
4th                              lua-markdown
a10disp                          lua-messagepack
acl                              lua-msgpack-native
acpica                           luaossl
acpid                            lua-periphery
[...]
$ ls -1 | wc --lines
1977
0

Copy this:

altercd() {
    cd() {
        unset -f cd
        cd "$@" && ls
        altercd
    }
}; altercd

Now you just can do simple cd:

cd / 
(files listed)
cd /home 
(files listed)
etc... 
  • 1
    Why not just do builtin cd?? – G-Man Says 'Reinstate Monica' Dec 19 '21 at 07:52
  • Because builtin cd will not do automatic ls when entering a folder, that is what is asked on the question. – Luciano Andress Martini Dec 21 '21 at 17:29
  • Sorry, I wasn’t clear.  Why do you have function1 that defines function2, and function2 undefines itself and then calls function1 (causing function2 to be defined again)?  I guessed that you constructed that Rube Goldberg machine as a way of allowing a function called cd to call the actual, built-in “change directory” directive rather than calling itself recursively — which you could have more easily accomplished with cd() { builtin cd $*; ls; }. … (Cont’d) – G-Man Says 'Reinstate Monica' Dec 21 '21 at 19:29
  • (Cont’d) …  Of course, if you *had* done that, it would have been essentially the same as frabjous’s answer from ten years ago (i.e., eight years before you posted your answer).  So, do you have any good reason for doing something fairly simple and easy in a complicated and hard way? – G-Man Says 'Reinstate Monica' Dec 21 '21 at 19:29
  • (Cont’d) …  And I see now, by looking at the revision history, that you originally *did* use builtin cd; i.e., the first version of your answer was, essentially, a flawed copy of frabjous’s answer (flawed in that it used $* instead of "$@"), but then you rewrote it to be what it is now in an attempt to be POSIX-compliant (because POSIX doesn’t support the builtin command). … (Cont’d) – G-Man Says 'Reinstate Monica' Dec 21 '21 at 19:30
  • (Cont’d) … But then later you deleted the explanation that you were jumping through hoops to be POSIX-compliant.  And I guess you forgot about that, inasmuch as that would have answered my question — you avoided builtin cd to be POSIX-compliant.  Which leaves me wondering why you deleted the explanation. … … … … … … … … … … … … … … … … … … P.S. I’m not 100% sure, but I believe that command cd would work, and it is POSIX-compliant. – G-Man Says 'Reinstate Monica' Dec 21 '21 at 19:30
  • In really I did not have thinked when I writed this question about using the builtin cd (and neither about being posix compilant) or even about command cd, when I wrote this I just see that the function was working, and thinked it was good enough. But your solution would be times better. – Luciano Andress Martini Dec 23 '21 at 13:09
  • I’m not sure I understand what you’re saying. When you first posted this answer (three years ago), you used builtin cd, and then 35 minutes later you posted the first altercd version, announcing that it was POSIX compatible. – G-Man Says 'Reinstate Monica' Dec 24 '21 at 01:36
  • Yeah I think I did not remember. Sorry man It was years ago! So you think I can use command cd that is POSIX? – Luciano Andress Martini Dec 27 '21 at 15:08
-3

No need to create functions altering the actual command. Its better to write this way in one line:

cd /myfolder; ls;

This will do the job as it executes 2 commands, one after the other in a single line.

  • 2
    Usually when someone says they want a command to run automatically, that means they don't want to type it out. – muru May 20 '23 at 05:41
  • but that is for limited cases. What if I want to execute an unusual command?? I cant define it all as the first one. That may otherwise define that command as whole and I would not be able to use it in sth else when I want only the forst one to run. – Humza Config May 20 '23 at 05:47
  • 1
    That may be what you want, but that's not what this question is about. – muru May 20 '23 at 06:29
  • 1
    If you changed that ; to && you could possibly justify this answer more easily. But even so it doesn't seem like it would do what the asker wants – Chris Davies May 20 '23 at 08:25