I'm dealing with java projects which often result in deeply nested folders (/path/to/project/com/java/lang/whatever, etc) and sometimes want to be able to jump, say, 4 directory levels upwards. Typing cd ../../../.. is a pain, and I don't want to symlink. Is there some flag to cd that lets you go up multiple directory levels (in my head, it would be something like cd -u 4)? Unfortunately I can't find any man page for cd specifically, instead just getting the useless "builtins" page.
17 Answers
A simple function, with an alias, too:
function cd_up() {
cd $(printf "%0.s../" $(seq 1 $1 ));
}
alias 'cd..'='cd_up'
(You could define this in ~/.bashrc
if you want it in every instance).
It's simple to use:
$ cd.. 10
-
-
4Good answer; you might want to point out that this is better than solutions such as
eval $(printf "cd ..;%.s" $(seq $1))
whichcd
multiple times, because~-
aka$OLDPWD
is set to the working directory from before invocation, rather than to an intermediate directory. P.S. - it simplifies tocd $(printf %.s../ $(seq "$1"))
if you want. Mar 15, 2017 at 13:45
Or... try this: (yay Google)
Navigate up the directory using ..n :
In the example below, ..4 is used to go up 4 directory level, ..3 to go up 3 directory level, ..2 to go up 2 directory level.
Add the following alias to the .bash_profile and re-login.
alias ..="cd .."
alias ..2="cd ../.."
alias ..3="cd ../../.."
(etc)
See Hack #2
-
12I would rather use the aliases
..
,...
and....
as they are faster to type, but the principle of aliases is the same of course.– BernhardJul 16, 2012 at 20:24 -
1@bernhard You're more than welcome to, as I don't believe it would cause any conflicts. I just left it as-is because I quoted it from the website. Jul 16, 2012 at 20:25
-
1The 4DOS.EXE shell for DOS also supported
cd ...
,cd ....
etc. It was very handy.– AlexiosJul 25, 2013 at 21:43 -
1
-
I would argue that
....
is faster than..3
, since it's a repeated keystroke. And I'm not likely to navigate up more than 3 directories at a time. In fact, I normally only use..
or...
, since I can't mentally walk my file system in jumps of 3 or more. Jan 13, 2020 at 16:36
A simple, low-tech solution that doesn't need any setup. Only works in shells with bash
-style command editing, though.
- Type
cd ..
- Press Up-arrow Return as many times as needed. Very fast if you use two fingers.
-
2
-
Turns out the correct answer is 'cd +n', where n is the number of levels you want to go up. Too bad this isn't documented anywhere!
-
22
-
5Doesn't work on bash 4.2.25 as shipped with Debian. Out of curiosity, what *nix and/or shell are you using?– AlexiosJul 25, 2013 at 21:44
-
Bash has a built-in called
dirs
that takes [+n] as an argument, and prints the nth unique directory that was added viapushd
. If youalias
cd
topushd
, then you can use this. However, note that this is not technically an answer to the OP's question, because this has to do with unique directories, meaning that the order gets messed up over time, as you return to the same directories. Oct 15, 2013 at 18:07 -
Yeah,
pushd
andpopd
also take a [+n] option, which is why that is able to work. Also, though it's imperfect and indirect, I guess this can be used as an answer to the question. Often you visited the directories above you recently, so they will be in the last 10 dirs stored by the directory stack. Oct 15, 2013 at 18:15 -
You could write a function (it has to be a function, as you want to change the state of your shell itself, namely the working directory; an external command would affect only its own process).
Here's a function that will go up a number of levels passed as argument (default 1):
up() {
# default parameter to 1 if non provided
declare -i d=${@:-1}
# ensure given parameter is non-negative. Print error and return if it is
(( $d < 0 )) && (>&2 echo "up: Error: negative value provided") && return 1;
# remove last d directories from pwd, append "/" in case result is empty
cd "$(pwd | sed -E 's;(/[^/]*){0,'$d'}$;;')/";
}
Use it like this:
up 4
-
1While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. Mar 13, 2017 at 16:01
-
1
-
To be nitpicky, isn't
up 4
really equivalent tocd /some/dir/
assuming$PWD = "/some/dir/that/is/deeply/nested/"
? Aug 15, 2018 at 23:28
Not exactly what you're asking for but you should look into pushd
and popd
. I find them much more useful for folder navigation than some cd...
alias
If you're going back and forth from a couple fixed areas, the usual thing is to have aliases.
alias proj1='cd /some/dir/containing/proj1'
alias proj2='cd /some/deeper/dir/structure/containing/proj2'
Instead of using aliases you could also use the following bash function:
function mcd() {
up=""
for ((i=1; i<=$1;i++)); do
up="${up}../"
done
cd $up
}
(or as a one-liner: function mcd() { up=""; for ((i=1; i<=$1;i++)); do up="${up}../"; done; cd $up; }
)
Adding this to your ~/.bashrc
file will make it available in your terminal and the building of a String ../../../../../../
before calling cd
will also make it possible to use cd -
to jump back to the start directory.
A more helpful implementation could also contain some user-input checks:
function mcd() {
if [[ $1 -lt 1 ]]; then
echo "Only positive integer values larger than 1 are allowed!" >&2
echo -e "\n\tUsage:\n\t======\n\n\t\t# to go up 10 levels in your directory\n\t\tmcd 10\n\n\t\t# to go up just 2 levels\n\t\tmcd 2\n" >&2
return 1;
fi
up=""
for ((i=1; i<=$1;i++)); do
up="${up}../"
done
cd $up
}
The syntax proposed in the answers are frankly unintuitive and meh.
You should be able to go back simply by repeating the period i.e. cd ...
or cd ....
https://github.com/parkercoates/dotfiles/blob/master/.zsh/expand-multiple-dots.zsh
or
Do you know about autojump? It's a third party hack, but can be useful in your scenario.
Try the rarely used environment parameter CDPATH
. Then you might not have to explicitly set the level.
Example:
$ find workspace -type d
workspace
workspace/project1
workspace/project1/com
workspace/project1/com/java
workspace/project1/com/java/lang
$ CDPATH=".:~/workspace:~/workspace/project1:~/workspace/project1/com:~/workspace/project1/com/java:~/workspace/project1/com/java/lang"
$ cd com
$ pwd
~/workspace/project1/com
If working on multiple projects, you can make the CDPATH setting into a project specific environment file. And trigger it with a shim for additional automation.
I tend to use pushd and popd quite a lot. I tend to use CDPATH to let me hop between project trees rather than subdirs in a project - but at the moment I'm working on a lot of small projects, not a few big projects. :)
If you are previously on the target directory:
luna:/tmp % mkdir -p a/b/c/d
luna:/tmp % pwd
/tmp
luna:/tmp % cd a/b/c/d
luna:d % pwd
/tmp/a/b/c/d
luna:d % cd -
luna:/tmp % pwd
/tmp
luna:/tmp %
Take a look at DirB. It's a BASH script that allows you to create bookmarks as follows:
OPERATIONS
- s Save a directory bookmark
- g go to a bookmark or named directory
- p push a bookmark/directory onto the dir stack
- r remove saved bookmark
- d display bookmarked directory path
- sl print the list of directory bookmarks
Very simple, very effective.
Step further from @Grigory's answer:
function cd_up() {
if [ -z "$1" ]; then
cd ..
else
cd $(printf "%0.s../" $(seq 1 $1 ))
fi
}
alias 'cdd'='cd_up'
That is :
// go up 1 directory level
$cdd
// go up 3 directory level
$cdd 3
Got sick of this exact same problem and ended up writing cd-plus as a result. A single file CLI utility that lets you cd up multiple directories.
Usage
echo `pwd` # /foo/bar/foobar/baz/foobaz/bazfoo
d baz
echo `pwd` # /foo/bar/
Adding the below code to your .bashrc
will extend cd
's completion to honor three plus dot notation
_cd_dot_expansion() {
local path="${COMP_WORDS[COMP_CWORD]}"
if [[ "${path}" =~ ^\.{3,} ]]; then
local dots="${BASH_REMATCH}"
local levels="${#dots}"
local tail="${path##*...}"
local expanded_dots='..'
# the loop starts at two because the first set of dots is
# assigned to `expanded_dots` when it is initialized
for (( i = 2; i < "${levels}"; i++ )); do
expanded_dots+='/..'
done
path="${expanded_dots}${tail}"
COMPREPLY=( $( compgen -A file "${path}" ) )
return
fi
_cd
}
complete -o nospace -F _cd_dot_expansion cd
Once this snippet is run in your bash shell typing cd .../
and hitting <TAB>
will complete the path to ../../
and list the relative paths of all of the directories that are two levels up, for instance.
Update: bash-completion
must be installed to have access to the _cd
method referenced in the code above. It's installable via most command line package managers
For fish shell:
Since no one has made a solution for the fish shell, I'd like to give it a try.
Just making a Fish version out of the answers given here, so consider this, Fish Shell edition. 😎
1st solution is based from @Grigory K answer.
Solution #1
Fish version would be like this:
function cd_up
cd (printf "%.s../" (seq $argv))
end
alias 'cd..'='cd_up'
Solution #2 (using a loop)
function cd_up
for i in (seq $argv)
set -a data "../"
end
if test -n "$data"
cd (string join "" $data)
end
end
You could remove the test branch if you don't want to cover the case where you pass zero.
alternative for solution #2
function cd_up
for i in (seq $argv)
set -a data "../"
end
cd (string join "" $data)
end
And just add an alias to cd..
via alias 'cd..'='cd_up'
You can edit your fish config via: vi ~/.config/fish/config.fish
Please note I am not the first to suggest the pushd
approach in this thread - I believe that is Richard Homolka.
Use basic Unix tools, but change your strategy. Use pushd
and friends to keep a ring of directories. Return to them instantly. Don't count anything.
Use pushd <DIR>
to add a directory to your ring - this becomes your current directory.
Obviously, use cd
to navigate between directories without affecting your ring.
Empty your ring with dirs -c
. There will always be one remaining - your current directory.
Use dirs -l -v
to view your ring.
Remove directories from your ring with popd
or popd +N
or popd -N
.
Rotate your the directories around the ring with pushd +N
. If L is size of the ring, then for any j in {0..L-1}.
for ((i=0; i<L; ++i)); do pushd $j; done
is an idempotent operation on the ring - it returns the ring to its initial state. The size of the ring is always at least L=1, since the current directory is always the first entry.
Use push -n <DIR>
to insert directories at the position in the ring behind the current directory, so that the current directory does not change.
Finally, make aliases (e.g., pu
, po
, and dv
) that do not conflict which other aliases or commands that are already defined on your(!) environment.
In this example, I make directories {a,b,c} and add the three directories to my ring, which has one to start with - the current directory, for a total of four. I show that rotating your ring at any nonzero position four times in a row is idempotent. I demonstrate the commands mentioned above. I pipe unimportant output to /dev/null. I use curly braces {} to group command output redirection.
<0.o>mkdir -p a/b/c
<0.o>{ pushd a; pushd b; pushd c; } > /dev/null
<0.o>dirs -l -v
0 /home/f41k0r/a/b/c
1 /home/f41k0r/a/b
2 /home/f41k0r/a
3 /home/f41k0r
<0.o>pushd +3 > /dev/null; dirs -l -v
0 /home/f41k0r
1 /home/f41k0r/a/b/c
2 /home/f41k0r/a/b
3 /home/f41k0r/a
<0.o>pushd +3 > /dev/null; dirs -l -v
0 /home/f41k0r/a
1 /home/f41k0r
2 /home/f41k0r/a/b/c
3 /home/f41k0r/a/b
<0.o>pushd +3 > /dev/null; dirs -l -v
0 /home/f41k0r/a/b
1 /home/f41k0r/a
2 /home/f41k0r
3 /home/f41k0r/a/b/c
<0.o>pushd +3 > /dev/null; dirs -l -v
0 /home/f41k0r/a/b/c
1 /home/f41k0r/a/b
2 /home/f41k0r/a
3 /home/f41k0r
<0.o>pushd +2 > /dev/null; dirs -l -v
0 /home/f41k0r/a
1 /home/f41k0r
2 /home/f41k0r/a/b/c
3 /home/f41k0r/a/b
<0.o>pushd +2 > /dev/null; dirs -l -v
0 /home/f41k0r/a/b/c
1 /home/f41k0r/a/b
2 /home/f41k0r/a
3 /home/f41k0r
<0.o>pushd +2 > /dev/null; dirs -l -v
0 /home/f41k0r/a
1 /home/f41k0r
2 /home/f41k0r/a/b/c
3 /home/f41k0r/a/b
<0.o>pushd +2 > /dev/null; dirs -l -v
0 /home/f41k0r/a/b/c
1 /home/f41k0r/a/b
2 /home/f41k0r/a
3 /home/f41k0r
<0.o>pushd +1 > /dev/null; dirs -l -v
0 /home/f41k0r/a/b
1 /home/f41k0r/a
2 /home/f41k0r
3 /home/f41k0r/a/b/c
<0.o>pushd +1 > /dev/null; dirs -l -v
0 /home/f41k0r/a
1 /home/f41k0r
2 /home/f41k0r/a/b/c
3 /home/f41k0r/a/b
<0.o>pushd +1 > /dev/null; dirs -l -v
0 /home/f41k0r
1 /home/f41k0r/a/b/c
2 /home/f41k0r/a/b
3 /home/f41k0r/a
<0.o>pushd +1 > /dev/null; dirs -l -v
0 /home/f41k0r/a/b/c
1 /home/f41k0r/a/b
2 /home/f41k0r/a
3 /home/f41k0r
<0.o>popd +2 > /dev/null; dirs -l -v
0 /home/f41k0r/a/b/c
1 /home/f41k0r/a/b
2 /home/f41k0r
<0.o>popd +2 > /dev/null; dirs -l -v
0 /home/f41k0r/a/b/c
1 /home/f41k0r/a/b
<0.o>popd +1 > /dev/null; dirs -l -v
0 /home/f41k0r/a/b/c
1 /home/f41k0r/a/b
<0.o>pushd -n /home/f41k0r/a > /dev/null; dirs -l -v
0 /home/f41k0r/a/b/c
1 /home/f41k0r/a
2 /home/f41k0r/a/b
<0.o>popd > /dev/null; dirs -l -v
0 /home/f41k0r/a
1 /home/f41k0r/a/b
<0.o>dirs -c > /dev/null; dirs -l -v
<0.o>pwd
/home/f41k0r/a
cd ...
where the number of '.' would be the number of levels to go up. Would this cause a conflict that I'm not aware of?zsh
andOh My Zsh
which is a better alternative tobash
and then you can just docd ...
(multiple dots) like it should be