[comp.sys.att] Curious ksh hack

brant@manta.UUCP (Brant Cheikes) (03/08/88)

In a memo by David Korn describing ksh, there is a paragraph that
states:

"The ENV file can have an undesirable effect on performance.  Even if
this file is small, the shell must perform an open of this file.  If
large functions are placed in the ENV file they must be read in and
compiled even if they are never referenced.  If you only need the
startup file for interactive shells, then set your ENV variable to a
value which evaluates to a file name for interactive shells and to the
null string otherwise.  If you export the startup file name in the
variable START, then setting

   ENV='${START[(_$-=1)+(_=0)-(_$-!=_{-%%*i*})]}'

will only invoke the startup file for interactive shells since the
subscript evaluates to 0 only if the shell is interactive."

Now, I've done this and it works, but I can't quite figure out how.
I've figured out the following:

  1. $- evaluates to the shell args, for an interactive shell this
is something like 'is', otherwise just 's'.
  2. _$-=1 evaluates to _<shellargs>=1, e.g., _is=1
  3. _=0 is just _=0
  4. _$-!=_{-%%*i*} becomes _is!=_  (if shell args contains 'i') and
     _s!=_s otherwise.

So for an interactive shell, we get
  (_is=1)+(_=0)-(_is!=_)

At this point I'm lost.  Don't the parenthesized items invoke
subshells?  How does this turn out to be 0?  And how does it turn out
to be 1 if the shell is non-interactive?

Can anyone shed some light on how this little hack works?  At least
this little trick should be of general interest, since it's only
documented in the internal ksh memo, not any of the manuals I've been
able to find.
-- 
Brant Cheikes
University of Pennsylvania
Department of Computer and Information Science
ARPA: brant@linc.cis.upenn.edu, UUCP: ...drexel!manta!brant

marty1@houdi.UUCP (M.BRILLIANT) (03/12/88)

In article <340@manta.UUCP>, brant@manta.UUCP (Brant Cheikes) writes:
> In a memo by David Korn describing ksh, there is a paragraph that
> states:
> 
> "The ENV file can have an undesirable effect on performance....
> ....  If you export the startup file name in the
> variable START, then setting
> 
>    ENV='${START[(_$-=1)+(_=0)-(_$-!=_{-%%*i*})]}'
> 
> will only invoke the startup file for interactive shells since the
> subscript evaluates to 0 only if the shell is interactive."
> 
> Now, I've done this and it works, but I can't quite figure out how.

I've figured out how, but I can't make it work.

The manual entry for ksh says that the subscript in an array parameter,
that is, x in $START[x], is evaluated as an arithmetic expression.
In arithmetic evaluation, as described under the "let" command, you can
put subexpressions in parentheses and use = for arithmetic replacement.

In an interactive shell, $- usually evaluates to "is".  So the first
term in parentheses, (_$-=1), evaluates to 1 but also sets the
identifier _is equal to 1.  The second term, (_=0), evaluates to zero
and sets the identifier _ to zero.  The third term is apparently a
misprint for (_$-!=_${-%%*i*}).  The part before the != evaluates, as
previously defined, to 1.  The part after the != uses ${..%..} to
lop off any trailing substring in $- that contains an i, so that part
evaluates to _ (zero) in an interactive shell, but to _$- (=1) in a
noninteractive shell.  The != is true in an interactive shell, equals
1, but is false in an noninteractive shell, equals zero.  The sum of
the three terms is zero in an interactive shell but is 1 in a
noninteractive shell.

The desired effect is achieved if, as it says in the manual entry,
"command and parameter substitution is performed on the value [of $ENV]
to generate the pathname of the script ...."  On my system, substitution
doesn't seem to happen, so it doesn't work.

M. B. Brilliant					Marty
AT&T-BL HO 3D-520	(201)-949-1858
Holmdel, NJ 07733	ihnp4!houdi!marty1

Disclaimer: Opinions stated herein are mine unless and until my employer
            explicitly claims them; then I lose all rights to them.

rupley@arizona.edu (John Rupley) (03/12/88)

In article <340@manta.UUCP>, brant@manta.UUCP (Brant Cheikes) writes:
> >In a memo by David Korn describing ksh, there is a paragraph that
> >states:
> >If you export the startup file name in the variable START, then setting
> >   ENV='${START[(_$-=1)+(_=0)-(_$-!=_{-%%*i*})]}'
> >will only invoke the startup file for interactive shells since the
> >subscript evaluates to 0 only if the shell is interactive."
> 
> Now, I've done this and it works, but I can't quite figure out how.
> I've figured out the following:
>   1. $- evaluates to the shell args, for an interactive shell this
> is something like 'is', otherwise just 's'.
>   2. _$-=1 evaluates to _<shellargs>=1, e.g., _is=1
>   3. _=0 is just _=0
>   4. _$-!=_{-%%*i*} becomes _is!=_  (if shell args contains 'i') and
>      _s!=_s otherwise.
> So for an interactive shell, we get
>   (_is=1)+(_=0)-(_is!=_)
> At this point I'm lost.  Don't the parenthesized items invoke
> subshells?  How does this turn out to be 0?  And how does it turn out
> to be 1 if the shell is non-interactive?

Awesomely neat trick! For Korn shell arithmetic, the parentheses 
establish high precedence. The assignments within them are just that, 
giving the value "1" to "_is" and "0" to "_".  The value of the 
parenthesized expression is the assigned value.  The two-fold 
cleverness is using the results of the first two assignments in the 
logical arithmetic test of the rightmost term, and then merging in the 
logical result (false=1 if "i" is set (1 != 0), true=0 if not) when 
evaluating the full expression.  So the arithmetric expression 
evaluates:
	1 + 0 - 1		if "i" is a shell parameter
	1 + 0 - 0		if "i" is not a parameter

Attached is a shell script that shows what happens, if you run
	ksh -x script		for non-interactive shell
	ksh -i -x script	for interactive shell

> Brant Cheikes
> University of Pennsylvania
> Department of Computer and Information Science
> ARPA: brant@linc.cis.upenn.edu, UUCP: ...drexel!manta!brant

John Rupley
 uucp: ..{ihnp4 | hao!noao}!arizona!rupley!local
 internet: rupley!local@megaron.arizona.edu
 telex: 9103508679(JARJAR)
 (H) 30 Calle Belleza, Tucson AZ 85716 - (602) 325-4533
 (O) Dept. Biochemistry, Univ. Arizona, Tucson AZ 85721 - (602) 621-3929
----------------------------------------------------------------------
#script
#run under ksh
#	ksh -x script
#	ksh -i -x script
(( a1=(_$-=1) ))
(( a2=(_=0) ))
(( a3=(_$-!=_${-%%*i*}) ))
(( a=(_$-=1)+(_=0)-(_$-!=_${-%%*i*}) ))
b="${START[(_$-=1)+(_=0)-(_$-!=_${-%%*i*})]}"
echo
echo "INDEX= $a1 + $a2 - $a3 = $a   and  ENV=START[INDEX]=$b"

davek@heurikon.UUCP (Dave Klann) (03/22/88)

In article <1486@houdi.UUCP> marty1@houdi.UUCP (M.BRILLIANT) writes:

  [ Discussion about the startup variable, and how it works. ]

>The desired effect is achieved if, as it says in the manual entry,
>"command and parameter substitution is performed on the value [of $ENV]
>to generate the pathname of the script ...."  On my system, substitution
>doesn't seem to happen, so it doesn't work.
>
>M. B. Brilliant					Marty

I fought this one for a while too.  Then after playing with the script,
and watching it carefully, I realized that the variable START (or its
equivalent) must be exported for the hack to work.  "DUH!," I said to
myself.  But hopefully this posting will help others avoid the same
mistake.