If switching to zsh is an option, you could use global aliases there for that:
alias -g ...=../..
But in any case that's only expanded when ...
is recognised as a full delimited token on its own. It will be expanded in echo ...
or echo ...> file
or (echo ...)
but not in echo .../x
or echo ~/...
or echo "..."
).
Normal csh-style aliases, whether it's in bash or zsh are only expanded when in command position or after another alias expansion ending in a blank character. In zsh, though, if you set the autocd
option, entering ../..
or another directory as the command name gets turned into cd ../..
, so
$ set -o autocd
$ alias ...=../.. ....=../../..
$ ...
Would be a way to cd
into ../..
, but then again so would alias ...='cd ../..'
or ...() cd ../..
.
A better approach that I've been using for many years is to bind the . key to something that inserts /..
instead of .
if what's left of the cursor ends in ..
:
magic-dot() {
if [[ $LBUFFER = (|*[[:blank:]/]).. ]]; then
repeat ${NUMERIC-1} LBUFFER+=/..
else
zle self-insert
fi
}
zle -N magic-dot
bindkey . magic-dot
bindkey -M isearch . self-insert # restore . in incremental search
# so it doesn't exit isearch there
Then entering ...
gets immediately and visually transformed to ../..
and pressing . again changes it to ../../..
and so on. If in emacs
mode, ..
followed by Alt+4,. makes it ../../../../..
for instance (/..
appended 4 times) using the usual NUMERIC prefix handling.
It used to sometimes get in the way when copy/pasting things that contain ...
, but that's no longer a problem now that bracketed paste support has been introduced. To enter a literal ...
, you can press Ctrl+v before the third .
or use backslashes, quoting or anything that makes so what's left of the cursor ($LBUFFER
above) doesn't end in <blanks-or-slash>..
such as .\..
, ..\.
, whatever/'...'
...
An intentional limitation of the above is that for instance in sort -o...
, the ...
is not expanded. Use sort -o ...
instead. Same goes for cd '.../***'
-> cd ...'/***'
. You can change the test above to [[ $LBUFFER = (|*[^.]).. ]]
for the ...
to be expanded to ../..
in more contexts although that would increase the probability of it being expanded in places you wouldn't like it to.
We can make the numeric argument handling a bit more useful by allowing cd Alt+4.
to cd
4 levels up (expand to ../../../..
) for instance with:
magic-dot() {
if (( NUMERIC )) && [[ $LBUFFER = (|*[[:blank:]/:]) ]]; then
LBUFFER+=..
(( NUMERIC-- ))
fi
if [[ $LBUFFER = (|*[[:blank:]/]).. ]]; then
repeat ${NUMERIC-1} LBUFFER+=/..
else
zle self-insert
fi
}
zle -N magic-dot
bindkey . magic-dot
bindkey -M isearch . self-insert
As mentioned by @Gilles in comment, recent versions of bash have added limited support for editing the line buffer in commands bound to keys, so you could do something similar there with:
insert_before_cursor() {
# for the equivalent of zsh's repeat $1 LBUFFER+=$2
local i
for (( i = 0; i < $1; i++ )); do
READLINE_LINE=${READLINE_LINE:0:READLINE_POINT}$2${READLINE_LINE:READLINE_POINT}
(( READLINE_POINT += ${#2} ))
done
}
magic-dot() {
(( ${READLINE_ARGUMENT-1} > 0 )) || return
if [[ -v READLINE_ARGUMENT && ${READLINE_LINE:0:READLINE_POINT} = ?(*[[:blank:]/]) ]]; then
insert_before_cursor 1 ..
(( READLINE_ARGUMENT-- ))
fi
if [[ ${READLINE_LINE:0:READLINE_POINT} = ?(*[[:blank:]/]).. ]]; then
insert_before_cursor "${READLINE_ARGUMENT-1}" /..
else
insert_before_cursor "${READLINE_ARGUMENT-1}" .
fi
}
bind -x '".":magic-dot'
# work around a bug in current versions of bash for the numeric
# argument to work properly, that means however that you lose the
# insert-last-argument normally bound to Meta-. (also on Meta-_)
bind -x '"\e.":magic-dot'
You need bash 4.0 or above for $READLINE_LINE
(not $READLINE_LINE_BUFFER
as misspelled in some of the documentation), and $READLINE_POINT
, and 5.2 or above for $READLINE_ARGUMENT
, though as noted above it currently doesn't work properly yet.
...
as a part of a path (cat .../somefile
), b) using...
through a variable (f=.../foo; cat "$f"
), and/or c) non-file arguments that contain...
(git commit -m "let's see if this works..."
). Which isn't to say you shouldn't do that if you like the idea, just that there might be some gotchas that make it less generally useful than it could be.ln -s ../.. ...
:)