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.
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.
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.
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.
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.
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.
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.
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...)
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 :) .
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)
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.
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."
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)...
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.
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".
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.
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?
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.
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 .
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`.
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.
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.
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.
> 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.
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."
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:
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
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.
> 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.
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.
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'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:
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.
- 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)
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.
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).
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.
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.
'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
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!
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.
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.
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.
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
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
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 ?
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
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.
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).
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?
[1]: http://karolis.koncevicius.lt/posts/fast_navigation_in_the_c...