67

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.

13 Answers 13

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.

15
  • 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 9, 2011 at 18:58
  • 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 9, 2011 at 19:07
  • 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. Sep 10, 2011 at 0:37
  • 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, 2017 at 19:10
  • 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, 2019 at 11:00
56

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.

15
  • 1
    What exactly does [ -z "$PS1" ] && return do?
    – syntagma
    Jan 21, 2015 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, 2015 at 17:42
  • 3
    Another way to check for interactivity is to look for i in "$-": case "$-" in *i*) ;; *) return ;; esac.
    – Kusalananda
    Jan 9, 2017 at 19:38
  • 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, 2017 at 19:24
  • 1
    @Swivel There shouldn't be a reason to export PS1.
    – Kusalananda
    Sep 30, 2017 at 19:48
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.

3

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
3
  • I found that setting a variable in LsAfterCd will inject it into the current bash session. Using a variable to keep the directory allows this to be simplified.
    – Roger Dahl
    Jan 13, 2021 at 7:52
  • By "simplified" do you mean "get rid of the PrevDir file"?
    – Jack
    Jan 14, 2021 at 15:45
  • @Jack Yes: askubuntu.com/a/97766/356625 May 13, 2022 at 9:44
2

Why not add an alias to your .bashrc file?

Something like:

alias cdls='cd "$@" && ls'
6
  • @don_crissti A funtion and an alias are different things. So why not? Dec 16, 2015 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, 2019 at 1: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, 2019 at 23:18
  • 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 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.)
    – roaima
    May 20 at 8: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
}
5
  • 6
  • To allow a parameter for the ls command, I use function mycd { builtin cd $1 && ls $2 }. Now you can call the command e.g. mycd .. -la Feb 19, 2016 at 9:20
  • @ChristianSchulzendorff: better to use the quotes: function mycd { builtin cd "$1" && ls "$2" } .
    – enzotib
    Feb 19, 2016 at 16:58
  • Does not work, I just tried it. The file was empty, but after adding your code, nothing changed.
    – Black
    Jun 25, 2018 at 14:20
  • You need to start a new shell after making the change before it's available. But you can run the same code at the prompt of your current shell just as well.
    – tripleee
    Jan 12 at 18:53
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.

6
  • 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, 2021 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, 2021 at 16:29
  • @they Would be great if readonly would work on functions
    – hh skladby
    Dec 19, 2021 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, 2021 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, 2021 at 18:48
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.

1
  • 2
    if you just do cd - it will bring you to your last dir that you were in.
    – Ian
    Nov 13, 2017 at 12:25
0

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

cdls() {
  cd ${$#} && ls ${@:0:$#-1}
}
1
  • 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, 2019 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... 
9
  • 1
    Why not just do builtin cd?? Dec 19, 2021 at 7:52
  • Because builtin cd will not do automatic ls when entering a folder, that is what is asked on the question. Dec 21, 2021 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) Dec 21, 2021 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? Dec 21, 2021 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) Dec 21, 2021 at 19:30
-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.

4
  • 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 at 5: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. May 20 at 5:47
  • 1
    That may be what you want, but that's not what this question is about.
    – muru
    May 20 at 6: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
    – roaima
    May 20 at 8:25

You must log in to answer this question.

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