Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a Bash shortcut for traversing similar directory structures?

The KornShell (ksh) used to have a very useful option to cd for traversing similar directory structures; e.g., given the following directories:

  • /home/sweet/dev/projects/trunk/projecta/app/models
  • /home/andy/dev/projects/trunk/projecta/app/models

Then if you were in the /home/sweet... directory then you could change to the equivalent directory in andy's structure by typing

cd sweet andy

So if ksh saw 2 arguments then it would scan the current directory path for the first value, replace it with the second and cd there. Is anyone aware of similar functionality built into Bash? Or if not, a hack to make Bash work in the same way?

like image 208
Steve Weet Avatar asked Sep 06 '09 19:09

Steve Weet


3 Answers

Other solutions offered so far suffer from one or more of the following problems:

  • Archaic forms of tests - as pointed out by Michał Górny
  • Incomplete protection from directory names containing white space
  • Failure to handle directory structures which have the same name used more than once or with substrings that match: /canis/lupus/lupus/ or /nicknames/Robert/Rob/

This version handles all the issues listed above.

cd () 
{ 
    local pwd="${PWD}/"; # we need a slash at the end so we can check for it, too
    if [[ "$1" == "-e" ]]
    then
        shift
        # start from the end
        [[ "$2" ]] && builtin cd "${pwd%/$1/*}/${2:-$1}/${pwd##*/$1/}" || builtin cd "$@"
    else
        # start from the beginning
        [[ "$2" ]] &&  builtin cd "${pwd/\/$1\///$2/}" || builtin cd "$@"
    fi
}

Issuing any of the other versions, which I'll call cdX, from a directory such as this one:

    /canis/lupus/lupus/specimen $ cdX lupus familiaris
    bash: cd: /canis/familiaris/lupus/specimen: No such file or directory

fails if the second instance of "lupus" is the one intended. In order to accommodate this, you can use the "-e" option to start from the end of the directory structure.

    /canis/lupus/lupus/specimen $ cd -e lupus familiaris
    /canis/lupus/familiaris/specimen $

Or issuing one of them from this one:

    /nicknames/Robert/Rob $ cdX Rob Bob
    bash: cd: /nicknames/Bobert/Rob: No such file or directory

would substitute part of a string unintentionally. My function handles this by including the slashes in the match.

    /nicknames/Robert/Rob $ cd Rob Bob
    /nicknames/Robert/Bob $

You can also designate a directory unambiguously like this:

    /fish/fish/fins $ cd fish/fins robot/fins
    /fish/robot/fins $

By the way, I used the control operators && and || in my function instead of if...then...else...fi just for the sake of variety.

like image 56
Dennis Williamson Avatar answered Nov 15 '22 04:11

Dennis Williamson


cd "${PWD/sweet/andy}"
like image 25
Michał Górny Avatar answered Nov 15 '22 04:11

Michał Górny


No, but...

Michał Górny's substitution expression works nicely. To redefine the built-in cd command, do this:

cd () {
  if [ "x$2" != x ]; then
    builtin cd ${PWD/$1/$2}
  else
    builtin cd "$@"
  fi
}
like image 23
DigitalRoss Avatar answered Nov 15 '22 05:11

DigitalRoss