Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Things you can do with mkshadow
- X-seq: zsh-users 29228
- From: Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx>
- To: Zsh Users <zsh-users@xxxxxxx>
- Subject: Things you can do with mkshadow
- Date: Sun, 10 Sep 2023 11:21:40 -0700
- Archived-at: <https://zsh.org/users/29228>
- List-id: <zsh-users.zsh.org>
I'm not really sure where else to put this, so putting it here.
Briefly, the mkshadow utility creates a "shadow copy" of a function,
builtin, or command so that it can be locally redefined. (Anything
not a function or builtin is presumed to be a command.) The canonical
usage is something like
hello() { print Hello, world }
greeting() { hello }
howdy() {
{ mkshadow hello
hello() { print Howdy, pardner }
greeting
} always {
rmshadow
}
}
Calling rmshadow undoes everything back to the most recent call to
mkshadow, so the use of an "always" block is a good way to assure the
two are called in matching pairs. However, this isn't required. For
example, the local redefinition can unmask itself:
howdy() {
{ mkshadow hello
hello() {
print Howdy, pardner
{ rmshadow
greeting
} always {
mkshadow hello
}
}
greeting
} always {
rmshadow
}
}
This makes the internal redefinition of "hello" behave like a
locally-scoped function. Whenever it's called from "howdy" it removes
the shadow copy so that anything it might call sees the original
definition of "hello". It then restores the shadow so that "howdy"
can safely remove it again.
There's an extra nuance available, which is that mkshadow can be told
how to name the shadow copy.
howdy() {
{ mkshadow -s howdy hello
hello() { print Howdy, pardner }
greeting
} always {
rmshadow
}
}
Without the -s option, a new shadow copy is created on every call, so
for example in recursive functions you can end up with shadows of
shadows. With -s, a new copy is created only on the first call, but
the number of mkshadow calls is still remembered, so rmshadow will not
restore the original function until the full stack is unwound. This
is most useful when the redefined function wants to call the original
-- note how the shadow name is appended to the original name to create
the new function:
howdy() {
{ mkshadow -s howdy hello
hello() {
print Howdy, pardner
hello@howdy
}
greeting
} always {
rmshadow
}
}
Even without -s you can access the most-recently shadowed function by
capturing the value of $REPLY following the call to mkshadow.
howdy() {
{ mkshadow hello
local hellofn="hello@$REPLY"
hello() {
print Howdy, pardner
$hellofn
}
greeting
} always {
rmshadow
}
}
As a final hint, rmshadow also sets $REPLY to the name set by the most
recent mkshadow, which is handy for the locally-scoped function trick.
howdy() {
{ mkshadow -s howdy hello
hello() {
print Howdy, pardner
{ rmshadow
local shadow=$REPLY
greeting
} always {
mkshadow -s $shadow hello
}
}
greeting
} always {
rmshadow
}
}
For those not on zsh-workers, mkshadow can be found here:
https://raw.githubusercontent.com/zsh-users/zsh/master/Functions/Misc/mkshadow
https://raw.githubusercontent.com/zsh-users/zsh/master/Completion/Base/Utility/_shadow
It uses a new feature developed in March - June 2023, so to make it
compatible with current released versions as of this posting, run
sed -e 's/\.shadow\./_shadow_/g' < _shadow > _shadow-5_9
mv _shadow-5_9 _shadow
Messages sorted by:
Reverse Date,
Date,
Thread,
Author