Hacker News new | past | comments | ask | show | jobs | submit login
How to navigate directories faster with Bash (2015) (mhoffman.github.io)
329 points by _xrjp on April 22, 2021 | hide | past | favorite | 172 comments



Here is one additional bookmark solution [1]:

    export CDPATH=.:~/.marks/

    function mark {
      ln -sr "$(pwd)" ~/.marks/"$1"
    }

    mark @name      # create a bookmark
    cd @name        # jump to bookmark
    cd @<tab>       # list bookmarks
    cd @n<tab>      # auto-complete
    cd @name/<tab>  # can access sub-directories within bookmarks
Works best when bookmarks are prefixed by "@" or other special symbol. Additional advantage is that you can use the same function `cd` to navigate both bookmarked and regular directories.

[1]: http://karolis.koncevicius.lt/posts/fast_navigation_in_the_c...


Might be worth pointing out the author of the post you linked settled in the end on `cd` and `ls` in a different style of organization over bookmarks or `z` or any other tool.


Pretty sure this would change into the symlinks, not the linked directories. I.e. the $PWD would be under `~/.marks/`.


Personally - never noticed a problem, but should be easy to solve just in case:

    alias cd="cd -P"


I have seen many solutions like this but this one is by far the most lightweighted. Thank you for sharing.


This is AWESOME. Thank you.


I upvoted for this alone:

  Another neat short hand is !$ which is an alias for the last argument of the previous command. This can be handy if one creates a new directory and wants to change into it without typing the the directory again. So commands would be

  mkdir -p make/new/directory
  cd !$
You've no idea how many years I've been thinking "This is Linux fer gawd's sake. Surely there must be some easy way to make a new directory and 'cd' to it in a single command!" But was never able to find or guess the right incantation.


I have an "mkcd" function in my startup files:

mkcd() { mkdir -vp "$1" && cd "$1"; }


Wait, now I'm confused. I have:

mkcd() { mkdir -p "$@" && cd "$_"; }

Can someone please detail the differences between $! and $_? (Apologies if I'm missing something obvious)


  $ echo "test" > /dev/null && echo !$
  /dev/null
  $ echo "test" > /dev/null && echo $_
  test


You're thinking of $_ and !$, and I think they are equivalent - $_ is explicitly the last argument of the previous command, whereas !$ is a history expansion command. ! starts a new history expansion command, and $ is a word designator for the last word of the selected history entry. To quote man bash, " If a word designator is supplied without an event specification, the previous command is used as the event."

I would not that !$ is a bash-ism, whereas $_ works at least on zsh and ksh as well as bash.


     alias tcd='a=`date +%s`;mkdir "$a" && cd "$a"'


Yep, first thing I add to my dotfiles when I don't have it haha


+1, I do the same.


Me too


Typing Esc then . does the trick too.


Alt + . does the same thing as well, with the added advantage that you can keep pressing alt and then hit . several times to go through all the "last arguments" in your history one by one.

Further, holding Alt followed by numeric argument followed by dot, gives you an argument at a specific position. For example, Alt + 1, Alt + . copies the first argument.


Cool one. "Ctrl + R" also does the same thing to search through past commands and then "Tab" to select the command you are looking for.


May favorite bash "trick" is:

!-1:gs/hello/world

which runs the last command with all instances of hello replaced by world. !-1 can be any so called event designator, for instance !n to refer to any specific line number from history or !string for last command containing a particular string.

Seriously people, read the man page for bash. Your shell has so many cool features.


And if you only have a single instance to replace, you can use ^old^new^

$ echo "hello hello hello world" hello hello hello world $ ^hello^bonjour^ echo "bonjour hello hello world" bonjour hello hello world


I know that this expansion exists, but never use it in practice.

Why? The last command is more general, you can just drop the g from the command I wrote to only replace the first occurrence. Besides, :[g]s/old/new should already be familiar to anyone who has used vim, so that is almost like having to learn zero new things.


This is great for removing an option from a prior command such as unzip -t ...:

^-t


[ESC + .] and [ALT + .] are most definitely news ones for me. Thank you. Always learn something new here!



Thanks, I discovered C-o thanks to you (even though I have gone through the bash manual several times).

No, more having to press C-r multiple times just to get sequential group of commands from history.


Yes I was also impressed when I found out about this one. However I use it far less frequently than ALT-. . ALT-# is also useful in the frequent cases where I am constructing a complicated commmand but I have to run another command before the current one.

Also keep in mind that most interactive command-line tools are built upon readline and can understand all those bindings (think python REPL, mysql/psql...)


>However I use it far less frequently than ALT-.

Haha of course. I use it so frequently I can't imagine how anyone uses their shell without it.

>can understand all those bindings

I knew that but just being reminded helped me realize that C-o is probably going to be useful in the REPL as well. Thanks.


or man bash /Readline Command Names<Enter>


TIL! Oh this one is nice... now just to get my fingers to remember it.



I have been looking for this Thanks for sharing :)


Only in emacs editing mode.

Real programmers use set -o vi


I use that on test machines a lot and had put it in bashrc and heard some cussing from other devs so now I just have to type it every time I spend more than acouple seconds in a terminal :) .


I didn't know this. Really neat!


That's just magical


How does that work?


M-. is readline's yank-last-arg binding.


Nice.

Bash is more than just bash. Composition is a killer technique.


You just blew my mind.


Wow amazing! Thanks :)


what, what?


For this exact use case I defined the following many years ago in my bash profile:

    # create a new directory and enter it
    function mkd() {
        mkdir -p "$@" && cd "$_";
    }
And use it like: mkd foo


Beware that using "$@" there might not be what you want, as it passes space-separated arguments to mkdir.

What does: "mkd foo bar" do? It creates both directories, and cd's into "bar".

Might be worth checking that only one argument has been passed, or use "$*" which instead would treat all arguments as one parameter, and "mkd foo bar" would create a "foo bar" directory instead (and cd to it)

YMMV.


Thanks a lot for pointing this out. I will happily fix this now!


All Bash manuals and tutorials should start from explaining this issue. And I really mean it. I can't remember how many times I was bitten by it until I finally read it up.


I feel one should also install shellcheck, and follow it religiously while looking up every warning they get - to understand what it's about, why it's a problem, and why the solution / "correct" incantation is so.

It's also a good way to "learn by doing", which works for many.


I still have to read it up from time to time even though I know it already. The difference is huge.


In zsh, you use -> % take <directory> which makes the dir and cd's into it.


That isn't standard zsh behaviour. I suspect it comes from oh-my-zsh¹, but wouldn't be surprised if it isn't a bunch of the configuration bundles.

¹ https://github.com/ohmyzsh/ohmyzsh/blob/master/lib/functions...


The bash version works in zsh too.


escpae-dot (.) also works for this: "It also works to use !$ instead of escape-dot, but that is slightly harder to type, so I don’t use that anymore."

Wrote about it here: https://henrikwarne.com/2018/08/11/my-favorite-command-line-...


Which you can type as ALT-.


This is the `yank-last-arg` readline comment. Bound to M-. and M-_ by default.

"Insert last argument to the previous command (the last word of the previous history entry). With a numeric argument, behave exactly like yank-nth-arg. Successive calls to yank-last-arg move back through the history list, inserting the last word (or the word specified by the argument to the first call) of each line in turn."

https://tiswww.case.edu/php/chet/readline/readline.html#IDX9...

Its sibling is `yank-nth-arg`, bound to M-C-y. So if you want the 2nd argument from the last command, you can press M-1 followed by M-C-y (yeah, arguments are indexed from 0)...

https://tiswww.case.edu/php/chet/readline/readline.html#IDX9...


I got stuck with exactly that use case today and trying to figure out how I can enter directly to my dir without typing it again. Bash is no so neat at completions like Fish for example. However !$ does the thing and keeps my fingers healthy.

That's great set of tips BTW.


I know this is a bash thread but zsh/oh-my-zsh will replace !$ with the actual argument when you type a space so you can see and edit it before executing the command.


bash supports the same, also named magic-space. For bash you can chuck something like "Space: magic-space" in your $INPUTRC, although you'd probably want a guard(see Readline Conditional Constructs in the manpage).

FWIW, zsh without oh-my-zsh can be configured to do too this with "bindkey ' ' magic-space".


I hava a mkcd alias to create a directory and changing into it.


I have one called `enter`.

    enter () 
    { 
        mkdir -p "$1";
        cd "$1"
    }


Also take a look at M-. (Meta+dot) which does the same thing while interactively editing a command...


Yes, that's a good one! My way of remembering is "bang for my buck" -> !$


Is !$ the same as $_ ?



Looks like !$ is a readline feature and $_ is a shell variable?



I don't think !$ is properly understood as a readline feature. It's a sh/bash abbreviation.

!! -> last whole command

!!:1 -> first argument of last command

!!:$ -> last argument of last command

!$ -> abbreviation for above


mkdir -p foo && cd $_


The most important tool I found for switching directories is called "z". You just partially type a previously visited directory's name and it takes you there. It also keeps track of your history to rank candidates for popularity. https://github.com/rupa/z/ It's basically CDPATH on stereoids and doesn't require preconfiguration like CDPATH.


I largely despised the commandline until I discovered z. Then I realized there are ways to make the commandline actually usable and it became an incredible, useful and user-friendly tool for me.


Similar is https://lib.rs/crates/zoxide if you're a fan of rust based solutions :) .


I don't think zoxide keeps an eye on shell history or uses a shell hook to see which dirs you navigate to manually? It seems it collects history only through its own CLI?


You're wrong it watches what you do and where you go via the shell command history file.


The author missed to mention one of my favorite tricks to change back in to the last visited directory:

  cd -
Hint: Change into directory called '-' can be done by

  cd ./-
I also use the mkcd alias / function:

  mkcd() { mkdir -p "$@" && cd !$; }
This allows creating multiple dirs and cd into the first one.

  mkcd test1 test2 test3


I don't think this is documented anywhere that I've found, but

   git checkout -
checks out the previous branch you were on, similar to cd -


Big fan of git-aliasing this to `git back`. I actually use it a ton and that small amount of extra chars plus the "-" makes a pretty decent difference for me.


Speaking of extra characters, I'm pretty far in with the `git` commands. `git checkout` -> `g c`, `git status` -> `g s`.

I usually do `g c -` but I guess `g back` wouldn't be bad.


cd - is there, it’s one of my favs too


I use `z` as a less manual way to search common directories instead of changing $CDPATH. It's based on 'frecency'.

https://github.com/rupa/z


After trying such history-based automatic tools I found they lacked predictability for me.

So the very small 50 LOC kd was born:

    kd # jumps to project top level (based on .git/Gemfile)
    kd foobar "$PWD" # saves an entry named foobar pointing to $PWD
    kd foo # jumps to foobar
    cp qux $(kd foo) # expands to foobar's value
I should probably make it expand to absolute paths so that I could change "$PWD" to .

https://github.com/lloeki/dotfiles/blob/master/shell/kd


Nice, looks like mine from ~20 years ago

https://gist.github.com/pcdv/3c52d82f59fe9042793033d5e249b14...

    g .       # add current dir to bookmarks
    g         # list bookmarks
    g del     # open bookmarks in vi (should use $EDITOR ...)
    g <regex> # jump to first matching bookmark
Wonder how many monthdays I saved compared to colleagues still navigating manually :D


I use `autojump` (aliased as `j`), which sounds quite similar as it also records your most frequently visited directories. It's as simple as entering `j down` to enter /some/path/to/MyDownloadFolder.

For exploring massive file trees, a terminal file manager that allows you to preview the contents of subdirectories without entering them is huge time saver. I use `lf`.

https://github.com/wting/autojump

https://github.com/gokcehan/lf


Autojump is great. Another similar one with less functionality but a stripped down script is "bashmarks":

https://github.com/huyng/bashmarks


I use (and develop) `goat`. It's like a shortcut manager for aliases for directories you go to often. And a way to make "cd ...." work for any number of dots.

https://github.com/0mp/goat


zsh, oh-my-zsh and z are the must haves that I add to any new environment immediately. Z and similar tools is such an elegant solution to directory navigation.


Same, I don't know how people do without them.

I love Z but I cannot believe there isn't something better yet?

One thing I'd love is something that helps navigating the current git project easier.


Or you just use a shell like fish that already comes with a lot of useful defaults. No need to customize everything by yourself, just use what other smart people already created.

https://fishshell.com/


> No need to customize everything by yourself, just use what other smart people already created.

Every once and then I decide to use and love the fish shell, and I commit to using it exclusively for a few days. My first session always starts with 40 minutes trying to disable all stupid colors. I don't like my terminal to look like a kitsch Christmas tree! Then, I feel frustrated with "jumps" of the interface when editing multi-line command lines; inevitably leading me to quit fish and go back to bash. I'm obviously not smart enough to appreciate the defaults that these smart people have created.


Probably you are smarter then the people who created fish :)


I used bash for maybe 7 years before trying fish. I was productive in bash, and never really had any complaints. But fish made life so much easier once I got used to it and learned how to customize it to my liking.

It get critiqued a lot, but I'd suggest others try it out before dismissing it and see if it works for them.


Yeah, to echo -- this probably works for people new to the whole thing, but like many here, it's easier to train myself incrementally with individual features added to bash than to try to "start over."


I'm surprised autocd was not mentioned. I saw

  alias ..='cd ..'
  # etc ...
But not.

  shopt -s autocd
zsh also has this. This seems like the fastest way to navigate since you no longer need to type, or retype, cd.


Hell yeah, I've been missing this functionality since the Amiga shell days! I should have known it was in there somewhere.


Another one:

  cdof () {
    cd "$(dirname $1)"
  }
as in 'Change into Directory Of"

Changes into the directory of a file. This is useful when you're doing complicated commands with long path names, but realize it would be easier if you were to cd into that directory. Use in combination with <Alt+.>. Especially useful if the filename component is long:

  $ pwd
  => ~/
  $ ls -l path/to/whatever/deep/somewhere/2021-04-22-filewithverylongnamefoobarbaz.xyz
  $ cdof <Alt+.>
  $ pwd
  => path/to/whatever/deep/somewhere


bit longer, but if given a file, change to its directory

    function cd() {

        if [[ ${#} -eq 0 ]]; then
            builtin cd
            return
        fi

        if [[ -f ${1} ]]; then
            builtin cd $(dirname ${1})
        else
            builtin cd ${1}
        fi

    }


Tried in cygwin and was a no-go. Tried some other concoctions in stack-overflow without success. Then I whiped this one and it worked. With a warning that will be promptly subdue to >/dev/null

function gothere {

    cd $(dirname -z `find -iname $1`)

}


oooh; stealing this nice.


Good selection of tips, like Normille I didn't know $! even though I have read Bash's man page through more than once.

Note that many of these are not just for Bash. pushd/popd is near universal among shells and CDPATH is standard among SYSV shells.

The idea of a shortcut for cd .. and its repetitions is good, but I'd prefer alias p='cd ..', pp='cd ../..' and so on, since I balk at the idea of having to distinguish, e.g., ..... and ...., but I find pppp and ppp are easier to discriminate by eyeball.

The shells I use most often are bash and the Emacs eshell, and over the years I appreciate eshell more and more. It has a few footguns, but the availability of the various selection-narrowing libraries, dired and tramp are huge pluses. zsh can pretty much do anything bash can do and more, and it's default on Macos and Arch Linux.


I'm an old-timer so was introduced to Unix before bash was ubiquitous. Actually it was csh/tcsh which later introduced me to "interactive" command history/editing. With interactive command history/editing, the old tricks to avoid excessive typing became less essential.

But I still use some of the old techniques as they continue to save time/typing - particular "$!". And even though I've reduced my reliance on aliases, I'm so used to jumping between directories that "pd" (pushd) and "nd" (pushd +1) are some of the few I continue to find myself installing.

The other shell techniques (strictly speaking editing features) that I was surprised to find are generally less used these days are the shortcuts like CTRL-w to delete the word to the left of the cursor or CTRL-a, CTRL-e to navigate to the start and end of the current line.


Many of these Emacs/readline shortcuts work in 99% of Mac apps too, which is nice.

https://jblevins.org/log/kbd


> I'm an old-timer so was introduced to Unix before bash was ubiquitous.

Me too, my first contact with UNIX was with SunOS 4 in 1988, which had csh as its default shell. I don't think I switched to bash until I started administering a Linux box in 1999, although I did script in POSIX shells before then. Before I had a system I was in control of, my attitude to bash was that it was unportable, because of the major differences between its v1 and v2 syntax.


Is there a way to tell CTRL-w to break words at / as well as space? I often use it to remove a parameter from a command I've copy-pasted or got from the history, but for the cd use case it removes too much.

Edit: ESC followed by backspace seems to work.


Alt-Backspace


If I were to alias cd ../../ et al I'd probably go for .. 2.. 3.. 4.. or something


https://github.com/junegunn/fzf also has a “cd mode”, by default triggerable using Alt-C, which can be useful.


Binding Ctrl-R to `fzf` for fuzzy history search is a killer feature.


Yeah this is one of the first things I install on a new machine along with zoxide


In my opinion this replaces (and even does a much better job i.e. less typing in the end, less commands to remember) almost everything in the article, especially when combined with https://github.com/rupa/z/. (or the PS counterpart ZLocation). Might be the type of work of course, but ever since I started using this I can go by weeks without needing to manually type cd. After all most of my time is spent in the same directories anyway. And if not, I'm really not manually going to type it, not even tab it, if it can be fuzzy matched since that is nearly always faster. tldr; people do yourself a favor and stop cd'ing around.


I use jump commands:

export MARKPATH=$HOME/.marks function jump { cd -P $MARKPATH/$1 2>/dev/null || echo "No such mark: $1" } function ju { cd -P $MARKPATH/$1 2>/dev/null || echo "No such mark: $1" } function mark { mkdir -p $MARKPATH; ln -s $(pwd) $MARKPATH/$1 } function unmark { rm -i $MARKPATH/$1 } function marks { ls -l $MARKPATH | sed 's/ / /g' | cut -d' ' -f9- | sed 's/ -/\t-/g' && echo }

I think the original source is #http://jeroenjanssens.com/2013/08/16/quickly-navigate-your-f...

Another neat trick is to set you .inputrc to vi mode with

set editing-mode vi set keymap vi

and then put this in your .bashrc

bind -m vi-insert -x '"\C-h"':'"pushd . 1>/dev/null 2>/dev/null && cd ../ && echo -e ${LILA} $PWD && ls"' bind -m vi-insert -x '"\C-n"':'"popd 1>/dev/null 2>/dev/null && echo -e ${LILA} $PWD && ls"' bind -m vi-insert -x '"\C-b":"cd $HOME && echo -e ${LILA} $PWD && ls"'

This enables you to go a directory up using ctrl-h and go back using ctrl-n.


I have a few functions in my bashrc for fast directory navigation.

up <#> - cd .. # times

upto <dir> - find dir in the current path above you and cd to it

down <dir> - find dir in the current tree below you including inside any number of subdirectories and cd to it

https://github.com/robmccoll/dotfiles/blob/master/bashrc#L15...


I've been playing around with the tool FZF recently. It's speeding up my command line workflow quite a lot. I made a small script that allows me to open a fuzzy search for any command, so I can write the command f followed by something like vim, evince, cd, mpv... I can optionally give a starting directory and a first query but I rarely do it. I have it in my .bashrc, but should work in any posix compliant shell. It needs fzf and fd:

  # fuzzy search any command
  f(){
    ROOT_DIR="."
    [ -n "$2" ] && ROOT_DIR="$2"
    FZF_OUT=`fd . -H --color=always "$ROOT_DIR" --exclude "Software/"| fzf --query="$3"`
    [ -n "$FZF_OUT" ] && "$1" "$FZF_OUT"
  }
in my case Software/ is a directory with lots of files I don't need so I exclude it from my searches. I also like using ranger, it has very nice bookmarking and launcher features.


Honestly what made the most difference for me was learning that `cd -` is "go back".

Works for git too:

`git checkout -` (checkout previous branch)

`git merge -` (merge with previous branch; useful for pulling master before merging)


I exhort you all to take a look at Fasd:

https://github.com/clvv/fasd


No mentions of https://github.com/jarun/nnn yet?

I've really taken a shine to using a real file manager with lots of nifty features without any bloat.


Yes, nnn is completely navigation oriented with an extra focus on speed and remaining lightweight. The plugin autojump supports jump/autojump/zoxide.


Great job on nnn! I feel like I've only scratched the surface of what it can do but I still use it many times per day.


I do the following [1]:

- I define "cdn" to be what others call "mkcd", as then if I have a command line "cd foo" and it tells me that foo doesn't exist, I can just add the 'n' to the previous entry. I also overload "cdn" so that when not given any argument, it goes into the newest subdirectory in the current directory.

- "u", "uu", "uuu", "uuuu", "uuuuu" for going "up" that many levels, and unlike the aliases in OP, I define them as functions and if those are given an argument, they descends into the path from there: "u foo" is equivalent to "cd ../foo", "uu foo" to "cd ../../foo".

- I also have a function called "mvcd foo bar" that moves foo to bar and then goes into bar. "mvcdnewdir foo bar" that does the same but will create bar. (I'm pondering unifying them to a version that always calls mkdir -p)

- an alias "c" for cd [2]. The single letter messes with the history search though (ctl-r c space or ctl-r cd space ?), so it's not necessarily a good idea. `shopt -s autocd` that I just learned about here may be better.

- some functions for special locations, "cs" for ~/scratch, "cb" for ~/bookmarks, etc.

[1] see .bashrc at https://github.com/pflanze/chj-home [2] but I never use bash's "alias" since aside of being shorter it is always worse than a function (no way to extend it later to take parameters, and messes up live redefinitions)


cdn is a good idea. I'm going to steal it, and change it to ncd, so that all i have to do to add that 'n' is C-a to the begin of line.


There's a small benefit to adding the n after the cd: When my thought process goes like "I need to go into directory" I can already type cd, then "oh that doesn't exist yet" then I can type the "n", then I can type out the "foo". You could possibly argue though that "cd foo -n" would be the right thing to type then. Or, finish "cd foo" then ctl-a "n" as you say.


Even better, just do

n!!

(!! repeats the previous command).


Reminds me of the family of LISP functions to access specific elements of a list, for example `(caddr l)` being defined as `(car (cdr (cdr l)))`


I vote for creating a « mkcd » alias !!


Here's my answer to that, an 'nd' (newdir?) alias that calls this python script:

https://github.com/danwills/shell-scripts/blob/bb989985d270c...

This makes all directories with names provided by args, including spaces and multiple levels (slashes) at once. It then enters the resulting directory as well (when aliased including 'cd' as advised in the comment).


  # create a directory in the current directory and cd to it

  function mkcd() { mkdir -p "$@" && eval cd "\"\$$#\""; }


Why the eval? This works fine for me:

  mkcd(){ mkdir --parents -- "$@" && cd -- "$@"; }


I independently also have a stack of '...', '....' etc aliases almost exactly the same as this! Extremely useful I reckon!

Another thing that I've found surprisingly invaluable is an alias (i went for 'ccd') that figures out (via python, so a bit slower on the 1st run) what part of the argument is a valid directory and then cd's you there. It's very handy indeed when working with file paths all the time (a film-vfx-on-Linux use-case) otherwise you end up using backspace to delete the file part constantly! In fact, about that: hitting Esc-Backspace in sequence, should delete back to the next word separator instead of one char at a time.


No need for stacks of `..`. Use this instead:

function up() { for i in $(seq 1 $1); do cd ..; done }

# usage:

up # goes up 1 directory

up 2 # goes up 2 directories

up 42 # goes up 42 directories


Nice little helper function, thank you. What do you think about renaming it to '..'? For me this feels a bit more natural:

  function ..() { for i in $(seq 1 $1); do cd ..; done }

  ..    # goes up 1 dir
  .. 2  # goes up 2 dirs 
  .. 42 # goes up 42 dirs


For me once I moved to zsh and added `oh my zsh` it really became so much easier.

I like the autocomplete, the fact that when cd-ing a known path you can just type the first leter of each directory with slashes, showing the git branch in working dir.

Its the little things.


'oh my zsh' is fun, but they really throw the kitchen sink in there. I had to get rid of it because terminal startup speed and doing anything in large folders was incredibly slow

If all the components of 'oh my zsh' were compiled into the shell and shared data structures, I bet it would be as fast as bash and only limited by IO


I believe that a lot the stuff you like from oh-my-zsh is in fish natively. You might check it out sometime.


You should try out out presto sometime :) .


I just read the title and thought "Switch to Zsh".

Bash is so amazingly clunky in comparison.


Fish’s predictive suggestions are also greatly for quick navigation


I haven’t tested this exact scenario but I like doing something like this when needed

    mkdir -p path/to/{one,two,three}/done
To create:

* path/to/one/done

* path/to/two/done

* path/to/three/done


Particularly helpful for quickly appending something to a file name

    mv something{,.old}


Shell alternation globs do ranges too, so be careful :)

  echo -e {A..Z}{a..z}{0..9}/{A..Z}{a..z}{0..9}"\n"|wc -l
  45697601
Runs for a bit too.


We tried to somewhat mimic the finder experience when implementing directory navigation at Fig (https://withfig.com). Except of course, we make it all keyboard driven!

The methods listed in the article are almost certainly better if you know exactly which directory you need to go to (like a specific project repo, your bin etc) however often you don’t!

Another interesting tool is Z: https://github.com/rupa/z


On a Mac, Findspot supports fuzzy directory search, either as a key sequence or via the command line (my alias is "c").

https://gumroad.com/l/findspot

I experiment with many forms of automation and stick with few of them. Interface complexity has a cost; there's a reason keyboards don't have more keys. For me, Findspot is indispensable.


FWIW I learnt a lot of my Bash tricks from: https://www.commandlinefu.com/


One of my favorite custom bash function is to use up() function to abstract away all the cd ../../../ command.

Up 3 will put me 3 directories behind the current one.


I have been using this "magic cd" alias for a while. You just type c and then the name of a folder, and it will find the most commonly visited folder with that name from your history and send you to it. It also supports tab completion.

https://gist.github.com/iwalton3/3f9bfce510f959782404be25cab...


My preference is "u" for "up":

> alias u="cd .."

And on Dvorak, "u" is under the left index finger. Can't get faster than that ;)


I'm using this bash script that adds a web-browser like history management (going forward with "+" and backward with "-"). It's been used for a couple of decades now.

https://gist.github.com/euske/4f06a6e060131949085c493bd28a0c...

edit: url


This looks really useful, thanks!


Wow I’m just blown away by this.

Really cool trick to navigate directories fast I’m definitely going to add this to my .zshrc

Honestly commandline has made so many things easier for me that I don’t even use regular apps besides my browser for browsing web anyhow using commandline has really made me productive

And I always look for automating or making the workflow better

Anyways great post thanks for sharing:)


For Zsh, there are better stuff available. For example, you can use `cd +<tab>` to change to a past directory. And instead of aliases for `.....`, you can use:

    # Expand ... to ../..
    function vbe-expand-dot-to-parent-directory-path() {
      case $LBUFFER in
        (./..|* ./..) LBUFFER+='.' ;; # In Go: "go list ./..."
        (..|*[ /=]..) LBUFFER+='/..' ;;
        (*) LBUFFER+='.' ;;
      esac
    }
    zle -N vbe-expand-dot-to-parent-directory-path
    bindkey "." vbe-expand-dot-to-parent-directory-path
    bindkey -M isearch "." self-insert


Thanks for sharing this I’ll try this out


I happily use bashmarks to jump to well known directories

https://github.com/huyng/bashmarks

  cd /path/to/project1
  s p1 # sets bookmark
  cd /elsewhere
  g p1
It also autocompletes bookmarks.


I use zoxide: https://github.com/ajeetdsouza/zoxide

There're only few directories I need to change to daily. So when I have to visit them, I need to type only `z first_two_characters_of_the_directory_name`


These sorts of things are really useful and it's one of the reasons I think everyone should keep their dotfiles on github or similar so they can share their little tricks. Mine are just a compilation of stuff like this that I've picked up from other people over the years. (I even try to put attributions in there if I got them from friends)

My favorite one is c to change to my ~/code directory with tab completion from anywhere. So I can type c pr<tab><enter> to cd in to ~/code/project from anywhere. (Full disclosure This is zsh and I don't have a bash equivalent for that)

Another thing I can't live without and some people hate is ls -la anytime I cd in to a directory. In a gui, I get that visual feedback immediately and it's usually the first thing I type when I land anyway so I just have a function for it https://github.com/gpspake/dotfiles/blob/master/zsh/gpspake/...


Is it me or the elephant in the room is : you can't augment regular "cd" functionality for autocomplete (for example via fzf) without resorting to super intricate bash code ? And so one has to introduce a new command to "replace" cd ?


Those bunch of dots aliases are a bit ridiculous. What are you gonna do, sit and count dots?


I have a simple pair of functions which I use to go up a level based on typing the portion of the path I want to go up until:

  # When called as `argu string`, prints the closest ancestor directory which
  # contains "string". If none is found, or "string" is empty, the current
  # working directory is printed. This function is useful if you need to specify
  # a file as an argument, but the relative path to it involves moving several
  # directories up from the current directory. For example, instead of doing:
  #  $ pwd
  #  /this/is/a/very/long/directory/path/for/demonstration/purposes/
  #  $ ls ../../../../another/directory/something.log
  # You could do:
  #  $ ls `argu dir`/another/directory/something.log
  argu() {
    if [[ "$1" == "" ]]; then
        echo "`pwd`/"
        return
    fi
    echo "`pwd | sed "s/^\(.*"$1"[^/]*\)\/.*$/\1/g"`/"
  }

  # When called as `cdu string`, changes directories into the closest ancestor
  # directory of the current directory which contains "string". If none is found,
  # or "string" is empty, no changes are made to the current directory.
  cdu() {
    cd `argu $1`
  }
I also put the following in ~/.zsh/completion/_cdu so I can get tab-completion of the portions of the path:

  #compdef cdu
  _cdu_parents=$(pwd | tr '/' ' ')
  _arguments "1:parents:($_cdu_parents)"
Or bash (in ~/.bash/completion/cdu):

  # bash completion for cdu

  _cdu()
  {
    local cur prev split=false
    COMPREPLY=()
    _get_comp_words_by_ref -n : cur prev
    _dirs=$( pwd | tr '/' ' ' )
    COMPREPLY=( $( compgen -W "$_dirs" -- "$cur" ) )
  }

  complete -F _cdu cdu

  # Local variables:
  # mode: shell-script
  # sh-basic-offset: 4
  # sh-indent-comment: t
  # indent-tabs-mode: nil
  # End:
  # ex: ts=4 sw=4 et filetype=sh


Also made mostly redundant by shopt autocd


I keep at least 3 in my aliases. So I go up up to 3 levels, and if I need to go further I type it in again.


Not a word about command editing.

Learn keyboard shortcuts to edit the current command line and you'll save years of your life.

ESC-. (dot) will get the last argument of the last command. This one alone will save you months.

CTRL-R will search backwards. This other will save more months.


I remember being advised that it was more efficient to use the command line editing shortcuts instead of changing directory.

That was over 10 years ago and I still use the shell with this in mind.

I’m not saying I never change directory, but once I do, I tend to stay there.

I had to scroll a fair way down to find a comment validating this opinion.

Perhaps we’re wrong when saying to change directory efficiently, you don’t.


Much faster. Using absolute (or home-relative ~) paths save a lot of time. Because now you can reuse commands a lot more and CTRL-R will yield more hits.


If you frequently need to switch between several git branches, pushb and popb are useful commands.

https://github.com/pwoolcoc/pushb


https://github.com/ranger/ranger nobody? And as others have pointed out, fasd and fzf of course.


Vim is a good way to browse directories also! Run "vim ." to see. It's nice because you can search folder names to jump to them (in the same way you'd search any other file).


And if you want to make bash history usable, use hstr [1].

[1] https://github.com/dvorka/hstr


> Maybe less well-known is that ~ is an alias for one’s home directory.

And ~mylogin is another alias for ones home directory.

And more generally, ~otherdev is an alias for otherdev's home directory.


This is accomplished with the zsh hash table I believe. `hash -d myproject=~/projects/myproject` will set up the same thing for ~/projects/myproject

$ cd ~myproject

$ pwd

~/projects/myproject


The cdable_vars bash option is among my favorites

  shopt -s cdable_vars
  p=/some/path/you/cd/to/a/bunch
  cd p


Figure I might as well share an alias I get some use out of.

  alias gd='cd "$(git rev-parse --show-toplevel)"'


I've heard so many horror stories about bugs related to CDPATH that I immediately assume it's a bad idea to have one now.


OT a bit, but while I don‘t use it often, sometimes fzf really takes the pain out of navigation.


Anther thing I regularily use is incrementally searching backward in the history with C-r.


Would it possible to alias the escape codes make by the up arrow as 'cd ..'


It is possible. Just source that.

   bind '"\e[A":"cd ..\n"'
But would you really want that rather showing previous command?


Cool, and duh about the previous command. I supposed some key that's not used for anything on the command line would be needed. Keypad up arrow instead of regular up arrow. Or a single ESC?


I know this is about bash, but for zsh, autojump is absolutely amazing.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: