Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Things you can do with mkshadow



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