How does the ksh ENV "trick" work?

Tim Peters tim at ksr.com
Mon May 27 18:28:13 AEST 1991


In article <1991May27.054451.24232 at brolga.cc.uq.oz.au> eric at brolga.cc.uq.oz.au (Eric Halil) writes:
> In "The Kornshell Command and Programming Language" by Morris I. Bolsky and
> David G. Korn, Prentice-Hall, 1989, ISBN 0-13-516972-0, page 78 they give:
>
>  export FILE=$HOME/.kshrc
>  # The subscript below evaluates to 0 when interactive.
>  ENV='${FILE[(_$-=0)+(_=1)-_${-%%*i*}]}'
>
>as a easy way to stop your .kshrc being evaluated when it's an not an
>interactive shell.  Can someone explain in detail how this works? 

Patience!  It's a long sequence of obscure tricks, and it helps to start
backwards.

So, to start at the end, if a varible VAR is defined in ksh, then
${VAR[0]} has the same value as $VAR (try it! -- a scalar variable in
ksh is treated much like a 1-element array).

Thus having set FILE to $HOME/.kshrc, ${FILE[0]} is also $HOME/.kshrc,
and ${FILE[1]} is undefined (returns a null string, so long as nounset
(-u) hasn't been enabled).

Next, ENV is evaluated when ksh starts up.  The man page is pretty clear
about this, so suffice it to say that if you can arrange that ENV
evaluate to $HOME/.kshrc if the shell is interactive, and to a null
string if the shell is not interactive, then you're done (if ENV
evaluates to a null string, no file will be read up since no file whose
name is a null string will be found).

So we're all set if ENV evaluates to ${FILE[0]} if the shell is
interactive and to ${FILE[1]} if it's not.  The guts of the long
expression does just that:

	(_$-=0)+(_=1)-_${-%%*i*}

evaluates to 0 if the shell is interactive and to 1 if it's not.

To see how that works, break the expression up so it's clearer:

	(_$-=0) + (_=1) - _${-%%*i*}

The first term is

	_$-=0

and this does two things:

1) As a crucial side-effect, sets to 0 the value of the variable whose
   name is the result of evaluating _$- (more on that below).
2) Evaluates to 0.

Recall that $- by itself evaluates to the set of flags with which the
shell was invoked.  E.g., $- is typically "ismh" for interactive shells
(try doing "print $-" and see what you get).  The precise set of flags
isn't really important except that it will have an "i" in it if & only
if the shell is interactive.

Executing the "_$-=0" part creates a new variable with underscore as the
first letter of its name and the set of flags as the remaining letters
in its name.  So, e.g., if $- is ismh, this part of the expression sets
the variable _ismh to 0.

Next, the term

	(_=1)

is evaluated.  This again does two things:

1) As a crucial side-effect, sets the variable whose name is a single
   underscore to the value 1.
2) Evaluates to 1.

Next the 0 from evaluating the first term is added to the 1 from
evaluating the second term, and 0+1 = 1.  So the long subscript
expression

	(_$-=0) + (_=1) - _${-%%*i*}

has so far been reduced to

	1 - _${-%%*i*}

Courage!  We're almost done <grin>.  The only remaining step is to
puzzle out what

	_${-%%*i*}

does.  First look at the ${-%%*i*} part (i.e., ignore the underscore for
a while).  ${-%%*i*} is just an ordinary instance of the "expand a
variable removing the longest matching suffix" construct:  it expands to
the value of $-, removing the longest suffix matching pattern *i* (note:
you could just as well use "##" instead of "%%" in this context):

  - If $- has an "i" in it, the longest suffix matching *i* is the
    entire string, hence ${-%%*i*} evaluates to a null string.

  - If $- does not have an "i" in it, no suffix matches *i*, hence
    ${-%%*i*} evaluates to $-.

Finally, the value of ${-%%*i*} is attached to _, and the resulting
variable is evaluated:

  - If $- has an "i" in it, _${-%%*i*} first evaluates to plain _, and
    the variable whose name is _ was set to 1 as a side-effect of
    evaluating the second term.  So the whole subscript expression
    evaluates to 1-1 = 0, and ${FILE[0]} = $HOME/.kshrc is read up.

  - If $- does not have in "i" in it _${-%%*i*} first evaluates to
    _$- (where the $- part is whatever set of flags the shell was
    invoked with), and the variable whose name is *that* was set to 0 as
    a side-effect of evaluating the first term.  So the whole subscript
    expression in this case evaluates to 1-0 = 1, and ${FILE[1]}
    evaluates to a null string and so no file is read up.

Revolting, isn't it <grin>?  And to think I used to enjoy stuff like
this ...

suspecting-obscurity-is-the-secret-end-as-well-as-the-means-ly y'rs  - tim

Tim Peters   Kendall Square Research Corp
tim at ksr.com,         ksr!tim at uunet.uu.net



More information about the Comp.unix.shell mailing list