I assume you're using the “new” completion system enabled by compinit
. If you're using oh-my-zsh, you are.
You need to tell zsh to use git branch names for gpDo
. Git already comes with a way to complete branch names. As of zsh 5.0.7 this is the function __git_branch_names
but this isn't a stable interface so it could change in other versions. To use this function, put this line in your .zshrc
:
compdef __git_branch_names gpDo
With this declaration, completion after gpDo
will only work after you've completed something on a git
command line at least once. This is due to a quirk of function autoloading in zsh. Alternatively, run _git 2>/dev/null
in your .zshrc
; this causes an error because the completion function is called in an invalid context, but the error is harmless, and the side effect of loading _git
and associated functions including __git_branch_names` remains.
Alternatively, define your own function for git branch completion. Quick-and-dirty way:
_JJD_git_branch_names () {
compadd "${(@)${(f)$(git branch -a)}#??}"
}
I'm not aware of any such utility. However, your approach is flawed (and I agree it's very hard to get it right).
The :a
modifier computes an absolute path without any access to the file system. In particular, it changes a/b/..
to a
regardless of whether a/b
is a symlink or not.
If you have a /a/b -> ../foo
symlink, that is /foo
only if /a
is not a symlink itself. Even if you canonicalize the $(dirname $1)
, that would still not work for symlinks like /a/b -> x/../../foo
if x
itself is a symlink.
In your case:
/tmp/example/A/B/C/D/symlink-0 -> ../../symlink-1/b/c/d/target
resolves to /tmp/example/A/B/symlink-1/b/c/d/target
only because neither /tmp/example/A/B/C/D
nor /tmp/example/A/B/C
are symlinks.
In other words, to get an absolute path to the target of a symlink that is free of ..
components, you may have to resolve more than one symlink and you cannot do it alone by combining the path of the file and the target of the symlink.
If you want such a path, the easiest (and I'd say only reasonable and/or useful) approach is to get the canonical path of that target of the symlink, that is where all the path components are neither ..
, .
nor symlinks except possibly for the last. For that, you could do:
zmodload -F zsh/stat b:zstat
canonicalize1() {
if [ -L "$1" ]; then
local link
zstat -A link +link -- "$1" || return
case $link in
(/*) ;;
(*)
case $1:h in
(*/) link=$1:h$link;;
(*) link=$1:h/$link;;
esac;;
esac
printf '%s\n' $link:h:A/$link:t
else
printf '%s\n' $1:A
fi
}
On your /tmp/example/A/B/C/D/symlink-0
symlinks, that still gives /tmp/example/a/b/c/d/target
, as the target of the symlink is not a symlink, but if that's not what you want, then I'd ask: what result would you want when doing:
canonicalize1 /tmp/x/y
Where /tmp/x/y
is a symlink to ../foo
and /tmp/x
a symlink to /a/b/c
and /a
, /a/b
, /a/b/c
are also themselves symlinks?
Best Answer
caffeinate
expects to execute a command in a new process.To interpret a
zsh
function, you need azsh
command.And you'd need to pass the definition of that function (as well as other functions it may need) to it, for instance with:
functions mysleep
dumps the definition of themysleep
function which we pass to that newzsh
for interpretation before calling the function, so thatzsh
invoked bycaffeinate
ends up interpreting:If we compare to
bash
's:(which is 2 characters shorter to type),
bash
will do:While with
zsh
, we get:I see several advantages of that latter approach:
%
characters (and even if it didn't, think ofsudo
for instance), we're not guaranteed guaranteed thatcaffeinate
will propagate it to thebash
command it runs.sleep
for instance in this example).bash
shell code is shorter, that's more data passed toexecve()
so contributes more toward the E2BIG limit of that system call.If you wanted to use the environment, you could still do:
In the case of
caffeinate
here, that is where we only need forcaffeinate
to run while the function is running, not necessarily for it to run the function, we can use other approaches like:cat
will run as long asmysleep
runs.mysleep
would still run in a separate process and that affects the stdout ofmysleep
though.would solve both problems.
As above, that creates a pipe between
mysleep
andcat
. But the writing end of the pipe is now on a newly allocate file descriptor above 10 (stored in$fd
) thatmysleep
will typically not write to.cat
will therefore read nothing but wait until end-of-file on the pipe which will only happen whenmysleep
(and all the children processes that inherit that fd) terminates.