Quickly navigate your filesystem from the command line
jeroenjanssens.com
jeroenjanssens.com
export MARKPATH=$HOME/.marks
function jump {
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- && echo
} function marks {
\ls -l $MARKPATH | tail -n +2 | sed 's/ / /g' | cut -d' ' -f9- | awk -F ' -> ' '{printf "%-10s -> %s\n", $1, $2}'
} function marks {
(cd $MARKPATH && stat -f"%-10SN%SY" *)
}
Even better with column (t="$(printf "\t")"; cd $MARKPATH && stat -f"%N$t%SY" * | column -ts"$t")Adding '-n' to the ls command works around this by causing the group id to be printed instead of the group name and all works, i.e.: \ls -ln "$MARKPATH" | tail -n +2 | sed 's/ / /g' | cut -d' ' -f9- | awk -F ' -> ' '{printf "%-10s -> %s\n", $1, $2}'
function _jump {
local cur=${COMP_WORDS[COMP_CWORD]}
local marks=$(find $MARKPATH -type l | awk -F '/' '{print $NF}')
COMPREPLY=($(compgen -W '${marks[@]}' -- "$cur"))
return 0
}
complete -o default -o nospace -F _jump jump complete -o default -o nospace -F _jump jump unmark
and then it simply replaces the existing _completemarks(), instead of needing both? function mark {
mkdir -p $MARKPATH; ln -s "$(pwd)" $MARKPATH/$1
}
Disclaimer: I'm not very good at bash, perhaps there is a nicer solution to this problem? function marks {
ls -l $MARKPATH | sed -E 's/ +/ /g' | cut -d' ' -f9- | sed -E 's/ -/ -/g' && echo
}
where the "\t" (looks like a bunch of spaces in the second sed statement) was typed in using [Ctrl] + [V] and then [Tab]. export MARKPATH=$HOME/.marks
function jump {
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 -if $MARKPATH/$1
}
function marks {
ls -l $MARKPATH | sed 's/ / /g' | cut -d' ' -f9- | sed 's/ -/\t-/g' && echo
}
_completemarks() {
local curw=${COMP_WORDS[COMP_CWORD]}
local wordlist=$(find $MARKPATH -type l -printf "%f\n")
COMPREPLY=($(compgen -W '${wordlist[@]}' -- "$curw"))
return 0
}
complete -F _completemarks jump unmarkI made something to the OP's system for the same reason of disdain for super deep trees at work. I'll make a gist and edit this post with it in a few minutes.
Edit: https://gist.github.com/desertmonad/6258429
I like the OP's idea of using symbolic links. That makes it more generally useful (i.e. a gui app can use those simple paths too). I may switch over to that.
Try removing the -i option to obtain what you want.
function unmark {
rm -if "$MARKPATH/$1"
}
Otherwise you are asking for much sadness.local wordlist=$(ls $MARKPATH)
_completemarks() {
local curw=${COMP_WORDS[COMP_CWORD]}
local wordlist=$(find $MARKPATH -type l -exec basename {} \;)
COMPREPLY=($(compgen -W '${wordlist[@]}' -- "$curw"))
return 0
} function _jump {
local cur=${COMP_WORDS[COMP_CWORD]}
local marks=$(find $MARKPATH -type l -printf "%f\n")
COMPREPLY=($(compgen -W '${marks[@]}' -- "$cur"))
return 0
}
complete -o default -o nospace -F _jump jump
Bash completion really needs better docs :-|Does anyone here know offhand how autocompletion in `zsh` works?
z is a wonderful complement to cd. After cd into a folders with z set up, a file stores all folders navigated to sorted by frecency, then simply jump to a folder with z [foldernameregex].
[Disclaimer: rupa's a good friend of mine]
[Disclaimer: rupa is a good friend of mine, as well]
[Disclaimer: I don't know rupa at all]
cdhere navigates your cmd.exe to wherever the current topmost Explorer window is pointed at.
It's not the same as the OP's trick, of course, but since you're voluntarily using Windows, I'm going to assume you "live" on the command line less than the average UNIX user, and more on GUI tools such as good old Explorer.
Not trying to take anything away from your script, it has a use-case, but just pointing out that there are alternate solutions for systems that don't have your script installed.
On Windows you can also get a nice "browse for folder" dialog in batch files with Wfolder [1]. If you replace "cdwhere.exe" in your script (cdhere.cmd) with "wfolder.exe" [2] and put wfolder.exe in the same directory as cdhere.cmd then instead of going to the location open in the topmost Explorer window the "cdhere" command will ask you where to cd, starting at your current location (like so: http://i.imgur.com/3MCzD4b.png).
Edit: Wfolder comes with a script that's analogous to cdhere.cmd.
[1] http://www.horstmuc.de/wcon.htm
[2] I did it in http://pastebin.com/aq0FiKmz.
On a different note, back in UNIX land I found myself doing the opposite of what you described: opening the file explorer to the same directory as the command-line. Of course, there is not much to that trick other than choosing an explorer that opens to the working directory or a path provided as an arguement.
FYI: On Windows 7, you need to press Alt+D to get to the location bar. (I believe that's the case for XP as well.) Ctrl+L doesn't seem to do anything.
Neither Ctrl+L nor Alt+D work for me (Win7 Ultimate, 64bit), but even with clicking manually this is extremely useful for me.
function ranger-cd {
tempfile='/tmp/chosendir'
/usr/bin/ranger --choosedir="$tempfile" "${@:-$(pwd)}"
test -f "$tempfile" &&
if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
cd -- "$(cat "$tempfile")"
fi
rm -f -- "$tempfile"
}
# This binds Ctrl-O to ranger-cd:
bind '"\C-o":"ranger-cd\C-m"'
(This script comes from the man page for ranger(1).)Edit: Modified the above script for use with zsh (and multiple users):
ranger-cd() {
tempfile=$(mktemp)
ranger --choosedir="$tempfile" "${@:-$(pwd)}" < $TTY
test -f "$tempfile" &&
if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then
cd -- "$(cat "$tempfile")"
fi
rm -f -- "$tempfile"
}
# This binds Ctrl-O to ranger-cd:
zle -N ranger-cd
bindkey '^o' ranger-cd
[1] http://ranger.nongnu.org/.it says it's not available. any idea why ranger is not longer available in package manager?
IMHO, just for jumping around directories, z is good enough.
I used to use z, but I've since switched to fasd. It's much like z, but also so much better. In addition to being able to do "z <part of directory>" you can do "f <part of file>". So, for example, if I've got a rails project I've worked on a lot recently I know its config.ru is high on frecency, so I'll just do "vim `f config`" to edit that file. If I want a file that's not so recent I can always do "vim `f -i config`" to pick from a list of files.
fasd is leaps and bounds above z in functionality, and I've thoroughly enjoyed using it.
_jump()
{
local cur=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(compgen -W "$( ls $MARKPATH )" -- $cur) )
}
complete -F _jump jumpGitHub: https://github.com/dictoon/marks
Edit: to install, unzip anywhere and add to your path. Then:
C:\Windows> mark win
C:\Windows> marks
win => C:\Windows
C:\Windows> cd ..
C:\> jump win
C:\Windows>
Marks will be stored in a marks\ subdirectory (relatively to the mark.bat file).With fish 2.0, the shell stores a list of "cd" commands issued from a specific directory, so mostly just typing "cd" yields an auto-completion hint out of the box.
Here's what I have done :
function ccb ()
{
FOO=`pwd`;
echo >> ~/shortcuts.sh;
echo alias $1=\'pushd $FOO\' >> ~/shortcuts.sh;
echo >> ~/shortcuts.sh;
. ~/shortcuts.sh
}
simple but quite effective.Now, all I have to do is type ccb and give a shortcut name to register a shortcut. Then whenever i want to jump to that directory, I just have to type the shortcut.
Eg :
foo@bar deeply-nested-dir# ccb deepd
foo@bar deeply-nested-dir# cd
foo@bar ~# deepd
foo@bar deeply-nested-dir#
My method lacks the a) delete shortcut and b) list shortcut features that the above solution gives, though. function ccb ()
{
FOO=`pwd`;
echo >> ~/shortcuts.sh;
echo alias $1=\'pushd $FOO\' >> ~/shortcuts.sh;
echo >> ~/shortcuts.sh;
. ~/shortcuts.sh
}PS: I assume you are calling shortcuts.sh in your .profile - that way aliases are available across sessions.
set MARKPATH $HOME/.marks
function jump
cd $MARKPATH/$argv; or echo "No such mark: $argv"
end
function mark
mkdir -p $MARKPATH; and ln -s (pwd) $MARKPATH/$argv
end
function unmark
rm -i $MARKPATH/$argv
end
function marks
ls -l $MARKPATH | cut -d' ' -f12-; and echo
endBonus: if you want autocompletion for the jump and unmark commands, just add the following lines (in zsh):
function _marks {
reply=($(ls $MARKPATH))
}
compctl -K _marks jump
compctl -K _marks unmark % cd ~/Dev/W3/m-r-r.github.io
% alias -g :blog="$PWD"
% jekyll build -d /tmp/out/ && cd /tmp/out
% ./index.html
% cd :blog
% pwd
/home/m-r-r/Dev/W3/m-r-r.github.io
% alias -g
:blog='/home/m-r-r/Dev/W3/m-r-r.github.io'
:dev='/home/m-r-r/Dev'
:c='/home/m-r-r/Dev/C'
:python='/home/m-r-r/Dev/Python'
:vim='/home/m-r-r/.vim'
In addition, this method works with any shell command: % pwd
/tmp/out
% make -C :blog publish
% git clone git://git.complang.org/ragel.git `echo :c`/ragel
% vim `echo :vim`/templates/\*.rl
On the other side, the aliases are not saved at exit unless you write a function
to do it: % tail -n 15 ~/.zshrc
function aliases {
(
alias -L -r
alias -L -g
alias -L -s
) | uniq
}
function save-aliases {
aliases > ~/.zsh_aliases
}
if [ -f ~/.zsh_aliases ]; then
. ~/.zsh_aliases
fimy method involves storing the bookmarks in a file but loading them into zshs directory hash. This means the directories are tab completable and if you have AUTO_CD set then you can change directory with simply: ~dir_name, otherwise: cd ~dir_name.
Frankly, this is far superior to the linked method.
alias .=cd
In /bin/sh "." sources a script. While this can be common in a script, one rarely does this interactively so I usurped . for the most common operation! alias ,=pushd
alias ,,=popd
alias ,.=dirs
Think of cd as a goto, pushd as a call, popd as a return and dirs as a stack trace! ..() { cd ../$* }
cdpath=(. .. ~/src ~)
Use of cdpath can be confusing so it is best to show part of $PWD in the prompt: PS1="%* %5. %h%(#.#.:) "
This ends the prompt with a # if you are running as superuser.These aliases/functions are enabled in .zshrc (only if run interactively -- that is, they are included below the following test):
if [[ ! -o interactive ]] ; then return; fi
The "benefit" of Jeroen's mark/unmark is that these paths persist (and can be used from any window running a shell. I have not found much need for that + I can have different $dirs in different windows. Also, given that my shell windows (under screen) persist for a very long time, I don't need symlink's persistence!Alternatively I define "." to be a function that does arg specific operation (cd if a dir, acroread if .pdf, vi if text etc.).
* Autojump automatically saves marks,
~~~ cd Downloads # And you are done. ~~~
* You don't have to think about mark names,
~~~ cd Projects/Python # It will be called `python`. ~~~
* You don't need to remember mark names,
~~~ j haskell # If you want `Projects/Haskell`, odds are the mark is auto-named `haskell`. ~~~
* You can specify only some part of mark name;
~~~ j bar # And you are in `Stuff/foobar`. ~~~
Other than that, this is, as I said before, great! Looking forward to go through the source code to learn more about Bash.
function mark {
mkdir -p $MARKPATH; ln -s "$(pwd)" $MARKPATH/$1
}alias here='pwd | pbcopy'
alias there='cd $(pbpaste)'
So now type `here` in current terminal window and type `there` in new terminal window to move to same directory!
[This works on Mac on other OS you could use xcopy or equivalent clipboard copy program]
As an example, the following two sandboxes contain the Broadcom Ethernet driver:
/build/username/sandbox_a/software/vendors/linux/linux-2.6.35.7/drivers/ethernet/broadcom /build/username/sandbox_b/software/vendors/linux/linux-2.6.35.7/drivers/ethernet/broadcom
Use the add alias command aa <alias name> <directory> to create aliases for the sandboxes:
aa a /build/username/sandbox_a aa b /build/username/sandbox_b
Then create an alias for the Broadcom directory:
aa brcm software/vendors/linux/linux-2.6.35.7/drivers/ethernet/broadcom
To change to the Broadcom directory in sandbox_a, just stack the appropriate aliases using the ua command. Stacking is done by stringing the aliases together with a period delimiter:
ua a.brcm
To change to the Broadcom directory in sandbox_b:
ua b.brcm
I don’t want symlinks to be resolved (/var/www becomes /private/var/www on os x) so I modified it to store the path in the file:
export MARKPATH=$HOME/.marks
function jump {
mark=$(head -n 1 "$MARKPATH/$1" 2>/dev/null)
if [[ $mark != '' ]]; then
cd $mark
else
echo "No such mark: $1"
fi
}
function mark {
mkdir -p "$MARKPATH"; echo "$(pwd)" > "$MARKPATH/$1"
}
function unmark {
rm -i "$MARKPATH/$1"
}
function marks {
find "$MARKPATH" -type f | while read filename
do
printf "%-12s -> %s\n" $(basename ${filename}) $(head -n 1 ${filename})
done
}
I think the if syntax is zsh so probably won’t work with bash. setopt autopushd pushdminus pushdsilent pushdtohome pushdignoredups
Which automatically pushes (unique) directories I've visted into $dirstack.Then I have an alias:
d () {
local dir
select dir in $dirstack
echo $dir
break
done
test "x$dir" != x && cd $dir
}
That gives me a list of directories to jump to in my history: ~/src/git/emacs-prelude $ d
1) /home/kelvie/tmp/typhoon 3) /home/kelvie/src/git
2) /home/kelvie/tmp 4) /home/kelvie
?#
So I just type 1-4 to go back to a directory I've previously been in.https://github.com/davejamesmiller/dotfiles/blob/master/.bas...
# record directory in ~/.lastpwd
record_pwd() {
pwd > ~/.lastpwd
}
chpwd_functions=(record_pwd)
# go to last directory when opening a new shell
if [[ -d `cat ~/.lastpwd` ]]; then
cd `cat ~/.lastpwd`
fi
For other shells that don't have something like chpwd_functions you could just alias cd.As of tmux 1.7, this should be unnecessary. Every tab and split will open in the working directory of the current pane, and the behavior can be changed with the `default-path` option.
alias ..='cd ..'
Moving up the file tree has never been easier! shopt -s autocd
If you type a directory in the command-line, it’ll cd into it, e.g.: $ .. # cd ..
$ ~ # cd ~
$ foo # cd foo
I can’t live without this.On OS X: "-bash: shopt: autocd: invalid shell option name"
Apple and their outdated tools... It gets annoying after a while.
setopt autocdJeroen
Jeroen
e.g., a command like "tar cfz bak.tgz ~3 ~2/subdirectory ~1" would mean something else once you've "pushd +3".
I always find it hard to undo my sequence changes to get the stack in the same order as before, so usually end up typing "dirs -v" a lot (or its alias) and renumbering my ~N references.
I have ported this functionality to Bash: http://thrysoee.dk/pushd/
I tend to spend my time in front of one. Is there an advantage to being behind it?
But jokes aside, I added this to my bashrc, looks useful.
Welcome to use, thanks for Jeroen's smart work.
https://github.com/parski/Wormhole
It works great! Great blog post.
# I make a point to always have the trailing slash,
# it makes life easier.
# Also, tab-complete works with this in Zsh.
a=/some/really/long/path/that/I/dont/like/typing/
b=/some/other/really/long/path/too/
cd $a
# ... do stuff ...
cp one two three $b
cd $b
# ... do more stuff ...
cp four five six $a
cd $b/even/longer/path
And so forth. Since it is a normal variable, you can use it anywhere you need the path. brew install fasd
Thank me later