I had to modify Jeroen's code to get the `marks` shortcut working under Mac OS 10.8:
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
}
One more fix... if you have group names with spaces this will throw the field number off and you'll see the modification time preceding the mark name.
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}'
I set my version up with a -f on the unmark rm so that I don't need to be prompted to remove it. Also added some basic completion for both jump and unmark
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 unmark
I recommend avoiding -f on rm if you can. Make one mistake and it can be painful. In this case think removing the -i from the unmark function will accomplish what you want.
I 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.
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.
Reminds me of z, which I use all the time. Anyone else use z? If you have not heard of it, get it now https://github.com/rupa/z.
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].
z is absolutely wonderful. Even my boss has become a convert, and he's been hacking unix since the 1970s, when he worked at AT&T, and is very set in his ways.
I have developed a very similar scheme for myself independently (haven't heard of z before, but thanks). A further idea is another command <code>e</code>, which is used like: <code>e reg1 reg2...</code>, and it will query z's database to see if there is a file in z's directory which matches the regs, and if found, open that file in Emacs. A similar command <code>f</code> will only print the path, so that <code>firefox $(f reg1 reg2)</code> will open the found file using firefox.
For Windows users I have a closely related shameless plug:
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.
What many people don't seem to know is that the location bar in Explorer (at least on Windows 8) can act as a run dialog, and it supplies the launched program with the current folder as a parameter. So simply hit Ctrl+L and type "cmd" or "powershell" or whatever command from your PATH and hit enter.
I have been using F6 in my browsers, and it does seem to focus the address bar in File Explorer too. You need to press Enter first to be able to type commands though.
I'm not sure voluntary is necessarily true. I have had to use Windows several times because we were working with Windows only programs.
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.
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.
If you want to reuse an existing cmd window you can type in "cd " then drag/drop the directory from any Explorer shell. You could also close the shell after finishing with it.
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.
I used to have a really hacky AppleScript to do the same thing in OS X. The crappy part was that launching the whole AppleScript runtime was really slow, but that was the only API I could find with getting that information from the Finder.
Yeah, but I rarely use the mouse when using either a file browser or the terminal, so switching to it is less convenient than a short terminal command.
You can also use ranger [1] to change directories, especially if you want to explore the directory tree quickly when you don't know exactly where to jump to. Install it and add the following to ~/.bashrc:
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
Good idea, but you shouldn't overwrite ctrl-O, which is a wildly useful function in bash: run current command in history and retrieve the next. If you want to re-run a sequence of several history commands, you can find the first in history (such as via ctrl-R reverse-isearch), then repeatedly hit ctrl-O to run it and retrieve the next command from history.
What is your distribution? I've just tried it and it's available in both Ubuntu 12.04 and Debian 7. The launchpad page says that it should be in later Ubuntu releases, too. Enable the "universe" repository then run apt-get update and see if it fixes the problem.
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.
I put z and fasd through their paces, and out of the box z works very intuitively. I like it. fasd takes a little more to get used to, but I'm going to put it through a full try out. Let's see if the additional features make it worth the while. In either case, thanks all for a great thread.
I don't know autojump, but from a cursory look: z is written in pure shell, is a single file, no external dependencies. autojump requires python, thus likely has more features.
IMHO, just for jumping around directories, z is good enough.
I've been using Bashmarks (https://github.com/huyng/bashmarks) for years, which appears to be basically the same thing but with a less verbose interface.
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.
cool!
I'm a command-line junkie as well.
So, I've been doing something similar for quite a while.
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.
Here's a `fish 2` shell version, that works on the Mac.
You can add it as `~/.config/fish/functions/mark.fish`:
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
end
I don't understand the advantage of using handwritten function instead of the
shell builtins.
The `mark` function could be replaced by `alias -g`, `jump` could be replaced
by `cd` and `unmark` could be replaced by `unalias` :
% 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
fi
my 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.
And with CDABLE_VARS set, you can simply do: dir_name
In fact, you don't even have to use the directory hash for this, you just do, e.g.: MY_DIR=/path/to/dir; MY_DIR
Frankly, this is far superior to the linked method.
Except tab completion is annoying since zsh will include commands and environment variables in its guesses. The linked method simply provides a way to store the directory hash across sessions.
I've been using the following in some form for over 3 decades!
(now in zsh)
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.).
I noticed that 'mark' doesn't work if you have spaces in a directory name which you can't avoid on the Mac (I tried to mark an interior folder inside the iPhone Simulator which is inside "~/Library/Application Support/iPhone Simulator/". Wrapping in quotes seems to do the trick:
function mark {
mkdir -p $MARKPATH; ln -s "$(pwd)" $MARKPATH/$1
}
This is a very informative thread for people trying to streamline their daily shell experience. I'm looking forward to experimenting with the utilities mentioned here. I spend a significant amount of time in, and moving between CVS sandboxes. All my sandboxes have the same internal directory structure, but the root of the path changes. To speed up navigation time I created a tool to replace directory names with aliases. The aliases are persistent. They can also be "stacked", which allows you to specify a sandbox as an alias, then reuse your existing aliases to navigate within the sandbox. Auto-completion in the shell is an excellent companion as well. More information on "stacked.aliases", as well as the source can be found here: https://github.com/ttomaino/stacked_aliases/wiki. I include one example to illustrate:
As an example, the following two sandboxes contain the Broadcom Ethernet driver:
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:
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.
I have no idea if anyone else does this, but I use tmux a lot for different projects. I usually have vim in one tab, logs in another, and tests in another. However, each tab I open I need to cd into the same directory. As such I came up with this helper for zsh. Every time I change a directory it records it. When I start a new session (new tmux or iTerm tab) it goes straight to that directory:
# 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.
> However, each tab I open I need to cd into the same directory.
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.
I have just added (1) quotes to the code, (2) a section about tab completion, and (3) a note for Mac OS X users. Many thanks again; HN is a great community!
Interesting idea. I've the bash built-in "pushd" and "popd" for a long time for similar reasons, but those effectively limit you to treating the history as a stack rather than an arbitrary list.
yeah, it can be annoying that the directory stack sequence keeps changing, which means that sometimes you can't easily re-run commands from your history if you've pushd since you last ran the command
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 frequent systems where tcsh, for historical reasons, is the default shell, and for all its faults it does have some nice pushd settings that make directory browsing with 'cd +<number>' and 'dirs' very convenient.
This reminds me of NCD (Norton Change Directory) which then went on to inspire many tools, like the excellent KCD and WCD. These tools index rather than create symlinks, though.
This is how I know I am old I create a symlinks in my home directory to oft used locations and get there with
cd ~/linkname
Has the added bonus of making my short path be ~/linkname rather than ~/some/very/deep/pocket/in/some/git/repo
Nice idea, reminds me of ncd from a far away, foggy past. I suggest some escaping on the directory name parameter though. If you're not careful, you're going to run into trouble with paths that have spaces or risk some nasty security issues with embedded special characters.
That looks clever and useful, I wasn't expecting that. I do maintain a few aliases to jump around, but this looks more efficient. Not sure though if I'll like "jump foo" better than simply "foo" that I use now, but maintaining those aliases is a pita.
I have a similar tool but it also manages shell environment variables, allowing you to jump around and/or switch working projects. You can check it out at: http://github.com/timtadh/swork
Without having me dig through the code, could you tell me in a few lines how it works? (not usage, but how internally you managed to handle shell environment vars, "clean after" finishing/switching, etc)
My Z-Shell predicts and auto-completes everything I do often perfectly, thus typing 2-3chars not only get's me deep into directories, but also do actions I need. But maybe the idea of folder bookmarks can still be useful, idk.
I'll do it all the time in my local shell session, along the following:
# 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.