I would find it very convenient to be able to use cd with a file argument.

cd myDirectory/anyname.anyExtension would be equivalent to cd myDirectory/

What would be the best alias or function to achieve this behavior ?

EDIT: Sorry I didn't mention it in the 1st place: I use zsh


In zsh, I often do:

cd /path/to/somefile(:h)

(h for head).

If somefile is a symlink, you can also do:

cd somefile(:A:h)

To get to the directory where the target of the symlink may be found.

The zsh equivalent of Chris' now bash-only solution would be:

cd() {
  [[ ! -e $argv[-1] ]] || [[ -d $argv[-1] ]] || argv[-1]=${argv[-1]%/*}
  builtin cd "$@"

In zsh, you can also redefine what "words" Ctrl-W removes.

In zsh, "words" in the context of the word-based motion/transpose/delete widgets are sequences of alnums plus the characters in the $WORDCHARS variable which by default includes /.

You could remove / from $WORDCHARS so that Ctrl-W only deletes one path component:


Another useful extension is the select-word-style widget which you can use to interactively choose between different word styles.

autoload select-word-style
zle -N select-word-style
bindkey '\ew' select-word-style

Then pressing Alt-W allows you to choose between different word styles.

$ cd /blah/blih<Alt-W>
Word styles (hit return for more detail):
(b)ash (n)ormal (s)hell (w)hitespace (d)efault (q)uit
(B), (N), (S), (W) as above with subword matching
  • Interesting. (I use zsh too). – Sébastien May 18 '13 at 9:17
  • Very interesting edit on the power of zsh and word styles. Thank you also for the zsh command. It does the job perfectly. – Sébastien May 22 '13 at 13:33

I assume you still want to retain the original functionality if you input a directory, and you are using bash.

cd() {
    local file="${!#}"

    if (( "$#" )) && ! [[ -d "$file" ]]; then
        builtin cd "${@:1:($#-1)}" "${file%/*}"
        builtin cd "$@"

If you are never going to use cd's options (-P, etc), then this will also suffice:

cd() { 
    if [ -d "$1" ] || [ -z "$1" ]; then
        builtin cd "$@"
        builtin cd "${1%/*}"
  • 4
    if you change the function name to cd make sure to add builtin in front of the cd calls – Ulrich Dangel May 17 '13 at 17:22
  • @Chris: You assumed right ;) Thanks for this ready to use function – Sébastien May 18 '13 at 9:21
  • The version that preserves cd options does not work with zsh. The simpler one works fine with zsh, BUT cd is no longer equivalent as cd ~ :/ – Sébastien May 22 '13 at 13:24
  • @Sebastien Try now, I think that should fix that. – Chris Down May 22 '13 at 14:20
  • @Chris, indeed adding a check on [ -z "$1" ] was sufficient. I have accepted Stephane's answer to give it more visibility, as it seems to be the best solution for zsh. – Sébastien May 23 '13 at 8:18

You could use dirname to strip the filename from the path, e.g.

mycd() { cd "$(dirname "$1")"; }

See man dirname.

  • 3
    Note that if you input a directory, this will change to the directory above that directory, which may not be desired (it's unclear from the question whether this is desired or not). – Chris Down May 17 '13 at 18:08
  • 1
    @ChrisDown True, but I left that as an exercise to the reader and your answer shows how to deal with it. – Adrian Frühwirth May 17 '13 at 18:10
  • 1
    @AdrianFrühwirth An exercise! Hah! – user13742 May 17 '13 at 18:24

If you add this to your .profile, then load it (source ~/.profile or log out and log in again), then mycd [file or directory] will take you to the right directory:

mycd() { if [ -d "$1" ]; then cd "$1"; else cd "$( dirname "$1" )"; fi ; }

If you name it cd, then strange things will happen.

  • 1
    This will break if the directory name contains whitespace, and you need a closing semicolon to terminate the command group. Edited it for you, hope you don't mind. – Chris Down May 17 '13 at 17:38
  • @ChrisDown, though that's true of other shells, you don't need the closing semicolon in zsh. – Stéphane Chazelas May 18 '13 at 9:42
  • Also, this breaks if passing args to cd, like -P. – Chris Down May 18 '13 at 11:37
cd2() {
 arg=() dir= cmd= IFS=" " msg='[-L|[-P [-e]]|-h] [dir|file]'

 while [ "$#" -gt 1 ] ; do
  case "$1" in
   -h) printf "%s: cd2 %s\n" "$0" "$msg"
    return 1 ;;
   *) arg+=("$1") ;;

 [ -f "${1:-}" ] && dir="$(dirname ${1:-})" || dir="${1:A}"
 [ ! -z "$arg" ] && cmd="cd ${arg[@]} $dir" || cmd="cd $dir"

 if ! $cmd 2>/dev/null; then
  printf "%s: cd2 %s\n" "$0" "$msg"
  return 1
  • Given a directory called $(sudo reboot), this function may reboot the system. Also, the user is using zsh, not bash. – Kusalananda Dec 2 '18 at 9:34
  • Give any command $(sudo reboot), this function will reboot the system. Also, +1 I didn't notice 'zsh' initially - good catch. – ecwpz91 Dec 3 '18 at 23:57
  • No, cd '$(sudo reboot)' would change directory, while your function would try to evaluate the name. – Kusalananda Dec 4 '18 at 6:17
  • Ah, I see and made changes. Let me know if that works? Good catch. – ecwpz91 Dec 5 '18 at 7:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.