22

I do a lot of work in Magento, and so do a lot of the people I work with, and it gets annoying to have to type:

cd ../../../../../../

To only find you're still a few directories from your root 'httpdocs' folder, so I'm trying to make a script that follows this pseudo-code:

while lowest level directory != httpdocs
    cd ../
end while;

Seems pretty simple, I'm assuming I would have to do some form of grep on checking if the last part of the current working directory is httpdocs, if not just keep going up a level until it's true.

I've been looking at learning bash for this, though it seems like there has to be something out there already that's similar, and the syntax puzzles me to no end.

Caleb
  • 70,105

5 Answers5

32

I have a function called upto in my .bashrc which allows you to go up to any directory on the current path by name:

upto ()
{
    if [ -z "$1" ]; then
        return
    fi
    local upto=$1
    cd "${PWD/\/$upto\/*//$upto}"
}

I also have completion for this function, so that it gives me valid directory names and completion when I hit tab:

_upto()
{
    local cur=${COMP_WORDS[COMP_CWORD]}
    local d=${PWD//\//\ }
    COMPREPLY=( $( compgen -W "$d" -- "$cur" ) )
}
complete -F _upto upto

In addition, I have another function jd which allows me to jump to any directory below the current one:

jd(){
    if [ -z "$1" ]; then
        echo "Usage: jd [directory]";
        return 1
    else
        cd **"/$1"
    fi
}

Example:

[/www/public_html/animals/hippopotamus/habitat/swamps/images] $ upto h[TAB][TAB]
habitat       hippopotamus
[/www/public_html/animals/hippopotamus/habitat/swamps/images] $ upto hippopotamus
[/www/public_html/animals/hippopotamus] $ jd images
[/www/public_html/animals/hippopotamus/habitat/swamps/images] $ 
Rob W
  • 928
dogbane
  • 29,677
  • You could also add an alias for upto httpdocs. – cjm Jun 03 '11 at 09:54
  • @drogbane: jd does not appear to work in every case, would you mind explaining how it works? – Cesar Jan 19 '12 at 06:22
  • @Cesar it uses globstar (**). To enable this feature in your bash shell use shopt -s globstar. – dogbane Jan 19 '12 at 08:37
  • 2
    This is so brilliantly done, I wish I could upvote twice. – droope Feb 02 '13 at 18:18
  • 1
    I have included a dynamic shopt detection and activation in the jd function, see https://gist.github.com/Rob--W/5888648. I've only tested it with Bash 4.2.45, so YMMV. – Rob W Jun 28 '13 at 22:28
9

This has to be a shell function, not a script, because a script executes in a new shell (and thus can't change the directory of the original shell).

function cdroot()
{
  while [[ $PWD != '/' && ${PWD##*/} != 'httpdocs' ]]; do cd ..; done
}

You can of course name the function whatever you like.

Some explanation: The first test ($PWD != '/') is a failsafe in case you do cdroot when you're not inside a httpdocs folder. It'll stop when you get to the root.

The second test (${PWD##*/} != 'httpdocs') is a bit more complicated. $PWD is a variable containing the path of the current directory. ${PWD##*/} trims everything up to and including the last slash.

cjm
  • 27,160
  • The downside to this iterative one is that you lose your ability to just do a cd - afterwards to go back to where you were. – Ezequiel Muns Jun 05 '13 at 23:45
7

The simplest bash function that you can put into your .bashrc that I can think of (simple because it does not use loops or external processes) is:

cdup() {
    cd "${PWD/\/httpdocs\/*//httpdocs}"
}

This uses the Pattern substitution parameter expansion to replace everything after /httpdocs/ in $PWD with `/httpdocs'. It looks very messy because we have to escape the forward slashes in the path to differentiate them from the forward slashes in the bash expansion syntax. Plus do not escape forward slashes in the replacement string, so it also looks inconsistent.

If there is no /httpdocs/ in the current path then no substitutions will be perform and it will just change to the current directory.

For example, the substitution will replace /a/b/c/httpdocs/e/f/g with /a/b/c/httpdocs and cd into that directory.

The function needs to be in your current shell environment (either as a function or alias) since a child process cannot change the current directory of its parent. This is why I say to put it in your .bashrc and not in a shell script.

camh
  • 39,069
  • Incidentally, it's worth mentioning that this will take you as high up in the hierarchy as possible (which is probably the most useful use case anyway). – Ibrahim Nov 14 '12 at 22:48
3

You could also put something like this in your profile:

alias cdh='cd $(echo $PWD | sed -e "s/\(.*\/httpdocs\)\/.*/\1/")'

The just typing cdh will get you to the topmost httpdocs directory in your current path.

Mat
  • 52,586
2

It doesn't completely address your problem, but it will save on doing cd ../../../../../../ a bit. I've borrowed this from user unknown's post here.


up N

jump N directories up in the directory tree

Instead of typing

cd ../../../..

you just type

up 4

and a

cd -     

will bring you back

Put the function into your .bashrc to use it.

# (c) 2007 stefan w. GPLv3            
function up {
ups=""
for i in $(seq 1 $1)
do
        ups=$ups"../"
done
cd $ups
}

boehj
  • 2,630