[comp.unix.questions] Forcing /bin/sh in a script under V/386 3.2 Korn shell

ned@pebbles.cad.mcc.com (CME Ned Nowotny) (07/19/89)

In article <14463@bfmny0.UUCP> tneff@bfmny0.UUCP (Tom Neff) writes:
>My thanks to all who have responded via mail or news posting to my
>question about forcing the Korn shell (KSH) to run a script using the
>Bourne shell (SH) instead of itself.  There have been no completely
>satisfactory answers, but I will summarize the State Of What Is Known.
>
... Lines Deleted ...
>
> * There is an exact analogue to what I want for the C-Shell (CSH):
>   if you put a colon ":" as the first line of a script, CSH will
>   detect it and automatically spawn SH instead of itself to execute
>   the script.  (This is the way AT&T documents it; some users claim
>   that putting *anything* other than "#" at the start of the first
>   line does it, or that a blank first line does it, but I recommend
>   sticking with the AT&T convention to avoid blowing up on someone's
>   weird CSH variant.)  You can see this done at the start of most of
>   the AT&T supplied system shell scripts like installpkg and shutdown
>   and even basename.  But no, it does not convince KSH.  To prove this,
>   create the script
>
>	:
>	ps -f
>

There is some missing history here.

In the old days (around Version 6 or 7 and before), the Bourne shell did not
have a comment character, but it did have the null command, ":".  Now, the null
command was useful in several ways (e.g. ":" is a trivial true command - this
was important when "true" was not a built-in and required a new process to be
forked).  In fact, one of the most useful things you could do with ":" was to
comment your shell script since ":" does nothing with the arguments (a.k.a.
comment string) that you may provide it.  While modern Bourne shells include
both the "#" comment character and a built-in "true", they also still include
the null command, ":".  As a consequence, the Korn shell also includes the
null command and its original semantics.  So no, it can not be used to
distinguish a Bourne shell script from a Korn shell script.

On the other hand, the C shell did use the "#" as a comment character in
the early days.  As a result, shell scripts that started with a "#" were
almost certainly C shell scripts beginning with a comment (as all good
code should).  Therefore, the C shell could be preferentially forked just
by determining whether the script began with a "#".  While BSD went on to
provide the more general script mechanism using "#!", AT&T never really
did much better than use the old comment character trick.  This even after the
Bourne shell gained the "#" comment character and despite the inclusion
of the C shell by UNIX vendors.  (Of course, AT&T did not include C shell
as part of its stock UNIX distribution for many years, so the conflict was
a non-problem from their point of view.)

So while I can't provide a solution to your problem given that your system
does not support the "#!" mechanism, I hope this history may at least explain
why things are the way they are (mostly, AT&T suffered from NIH - and yes,
BSD suffered from "featuritis").  Errors in the above will no doubt be quickly
corrected.


Ned Nowotny, MCC CAD Program, Box 200195, Austin, TX  78720  Ph: (512) 338-3715
ARPA: ned@mcc.com                   UUCP: ...!cs.utexas.edu!milano!cadillac!ned
-------------------------------------------------------------------------------
"We have ways to make you scream." - Intel advertisement in the June 1989 DDJ.

friedl@vsi.COM (Stephen J. Friedl) (07/19/89)

In article <1792@cadillac.CAD.MCC.COM>, ned@pebbles.cad.mcc.com (CME Ned Nowotny) writes:
> In fact, one of the most useful things you could do
> with ":" was to comment your shell script since ":" does nothing
> with the arguments (a.k.a.  comment string) that you may provide
> it.

While this is true, it can be misleading.  In particular, the line

	cat file file2 > file3

isn't commented out by prefixing it with a colon.  Cat doesn't run,
but redirection *will* take place and file3 will be truncated.

     Steve

-- 
Stephen J. Friedl / V-Systems, Inc.  /  Santa Ana, CA  / +1 714 545 6442 
3B2-kind-of-guy   / {attmail uunet}!vsi!{bang!}friedl  /  friedl@vsi.com

"Why can't I do pointer multiplication in C?" - Blair Houghton

ned@pebbles.cad.mcc.com (CME Ned Nowotny) (07/19/89)

In article <1152@vsi.COM> friedl@vsi.COM (Stephen J. Friedl) writes:
>In article <1792@cadillac.CAD.MCC.COM>, ned@pebbles.cad.mcc.com (CME Ned Nowotny) writes:
=>> In fact, one of the most useful things you could do
=>> with ":" was to comment your shell script since ":" does nothing
=>> with the arguments (a.k.a.  comment string) that you may provide
=>> it.
=>
=>While this is true, it can be misleading.  In particular, the line
=>
=>	cat file file2 > file3
=>
=>isn't commented out by prefixing it with a colon.  Cat doesn't run,
=>but redirection *will* take place and file3 will be truncated.

True.  This is probably one of the reasons an actual comment character (#)
was eventually added to the Bourne shell.


Ned Nowotny, MCC CAD Program, Box 200195, Austin, TX  78720  Ph: (512) 338-3715
ARPA: ned@mcc.com                   UUCP: ...!cs.utexas.edu!milano!cadillac!ned
-------------------------------------------------------------------------------
"We have ways to make you scream." - Intel advertisement in the June 1989 DDJ.

bob@wyse.wyse.com (07/20/89)

In Article 14223 of comp.unix.questions ned@mcc.com (Ned Nowotny)
writes:
<  In article <14463@bfmny0.UUCP> tneff@bfmny0.UUCP (Tom Neff) writes:
<  >My thanks to all who have responded via mail or news posting to my
<  >question about forcing the Korn shell (KSH) to run a script using the
<  >Bourne shell (SH) instead of itself.  There have been no completely
<  >satisfactory answers, but I will summarize the State Of What Is Known.
<  >
<  ... Lines Deleted ...
<  There is some missing history here.
<  
<  In the old days (around Version 6 or 7 and before), the Bourne shell
<  did not have a comment character, but it did have the null command, ":".
<  Now, the null command was useful in several ways ...
 ----lines also deleted----

My apologies as I did not see the original summary that Tom Neff put
out, which may have already included the solution I am sending for
getting ksh to exec sh.  Also, this is my first posting, so if I
have neglected some portion of netiquette, please let me know
via e-mail  :-).  I have also supplied some additional stuff
about uses of the colon command.

As noted, using the colon command for comments is a problem because
of the possible side effects.  I came across the recommendation, in
_Introducing the UNIX System_ by McGilton and Morgan, that it
is best to place single quotes around the arguments to prevent
these side effects.

The colon command is also useful for evaluation of variables, as in
the following:

	: ${CWDIR=`pwd`}

which will set CWDIR to the current directory if CWDIR is not already
set.  If the colon command were not used the shell would then
substitute this name and attempt to execute it as a commend.

With respect to the original question of Tom Neff's, perhaps the
following would help.  It depends on the fact that both _sh_ and
_ksh_ will execute the similar code, while _ksh_ adds some "extras", such
as RANDOM, which _sh_ will treat differently.  In _ksh_ each reference
to RANDOM will cause the value of RANDOM to change.

	if [ "$RANDOM" -eq "$RANDOM" ]
	then
	   # what you want sh to do
	else
	   # exec sh on this script
	   exec /bin/sh `whence $0` "$@"
	fi

The _whence_ builtin generates the full pathname of the current file
(assuming it is in the path) and _sh_ is used to run it.  The original
arguments must also be supplied to this new invocation.  Using the quoted
$@ above (if I understand it correctly), will guarantee that the
form of the arguments on the original command line will be passed
to the new script invocation in exactly the same form.

I picked up the general idea from some _ksh_ examples I saw once,
in _The Wizards Grabbag_ in UNIXWorld, but I am afraid I do not
remember the author, nor do I have the original at hand.  I have
used the method and found that it works as advertised.  (The
original was used to guarantee that _ksh_ was run on the script,
which contained _ksh_ specific code.)

Hope this proves useful :-)
Bob McGowan
Wyse Technology, San Jose, CA
..!uunet!wyse!bob
bob@wyse.com

guy@auspex.auspex.com (Guy Harris) (07/21/89)

 >While this is true, it can be misleading.  In particular, the line
 >
 >	cat file file2 > file3
 >
 >isn't commented out by prefixing it with a colon.  Cat doesn't run,
 >but redirection *will* take place and file3 will be truncated.

Furthermore, it means that the arguments to the ":" command are parsed,
so the shell won't do what you want if you try something like

	:
	: This isn't a comment
	:

(unless you want something rather perverse).

peter@ficc.uu.net (Peter da Silva) (07/23/89)

In article <2264@auspex.auspex.com>, guy@auspex.auspex.com (Guy Harris) writes:
> 	:
> 	: This isn't a comment
> 	:

Perhaps it's time for the Obfuscated Shell Script contest?
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Business: peter@ficc.uu.net, +1 713 274 5180. | "...helping make the world
Personal: peter@sugar.hackercorp.com.   `-_-' |  a quote-free zone..."
Quote: Have you hugged your wolf today?  'U`  |    -- hjm@cernvax.cern.ch

jon@jonlab.UUCP (Jon H. LaBadie) (07/24/89)

In article <2318@wyse.wyse.com>, bob@wyse.wyse.com writes:
	... Much deleted ...
> 
> With respect to the original question of Tom Neff's, perhaps the
> following would help.  It depends on the fact that both _sh_ and
> _ksh_ will execute the similar code, while _ksh_ adds some "extras", such
> as RANDOM, which _sh_ will treat differently.  In _ksh_ each reference
> to RANDOM will cause the value of RANDOM to change.
> 
> 	if [ "$RANDOM" -eq "$RANDOM" ]
> 	then
> 	   # what you want sh to do
> 	else
> 	   # exec sh on this script
> 	   exec /bin/sh `whence $0` "$@"
> 	fi
> 
> The _whence_ builtin generates the full pathname of the current file
> (assuming it is in the path) and _sh_ is used to run it.  The original
> arguments must also be supplied to this new invocation.  Using the quoted
> $@ above (if I understand it correctly), will guarantee that the
> form of the arguments on the original command line will be passed
> to the new script invocation in exactly the same form.

A few comments to strengthen Bob's excellent suggestions:

1. Some shells would generate a fatal error if the arguments supplied
   to the test operator "-eq" were non-numeric.  This is one case
   where the "=" operator might be better on numeric arguments.

2. In case the command being "exec'ed" has a strange name, like "-xyz",
   /bin/sh could still be forced to interpret it as a command with the
   "-c" option.

3. The meaning of "$@" varies with different versions of the shell IF
   there were no arguments on the original command line.  Some properly
   evaluate to no arguments, but some evaluate to one null argument,
   i.e. just like you passed the argument "".  If the program expects
   a file name or no arguments, this can cause problems.

   An alternative solution is a "special substitution" construction.

	${@:+"${@}"}

   This says if $@ is NULL, leave it alone, that is what I want.  But
   if it has a non-NULL value, evaluate to its quoted value.

Combining these comments and using the conditional operator rather than
an if statement; plus quoting $0 on general principles, we have:

  [ "${RANDOM}" = "${RANDOM}" ] || exec /bin/sh -c "${0}" ${@:+"${@}"}

  # what you want sh to do

Now a question for Bob.  Can you let me know the need for the

	`whence $0`

construction in your solution.  On inspection, it seems that $0 would
be sufficient as the script was invoked that way to begin with.  But
I am certain I am overlooking some subtle situation in which that
is not the case.
-- 
Jon LaBadie
{att, princeton, bcr}!jonlab!jon
{att, attmail, bcr}!auxnj!jon

merlyn@iwarp.intel.com (Randal Schwartz) (07/25/89)

In article <799@jonlab.UUCP>, jon@jonlab (Jon H. LaBadie) writes:
[lotsa stuff deleted]
| Combining these comments and using the conditional operator rather than
| an if statement; plus quoting $0 on general principles, we have:
| 
|   [ "${RANDOM}" = "${RANDOM}" ] || exec /bin/sh -c "${0}" ${@:+"${@}"}
| 
|   # what you want sh to do
| 
| Now a question for Bob.  Can you let me know the need for the
| 
| 	`whence $0`
| 
| construction in your solution.  On inspection, it seems that $0 would
| be sufficient as the script was invoked that way to begin with.  But
| I am certain I am overlooking some subtle situation in which that
| is not the case.

I can think of a subtle situation: the $ENV file of ksh sets a
different searchpath, or maybe even an alias, that would not be picked
up by the corresponding /bin/sh started later.

But also, you DON'T want the -c on there.  The command line would then
consist of just the command name, not the arguments.  (The shell
tosses arguments on a -c command line...)

Thus, a more secure initial line would be something like:

[ "X$RANDOM" = "X$RANDOM" ] || exec /bin/sh "`whence \"$0\"`" ${@+"$@"}

Note the "X"'s on the test... if $RANDOM is not set, the previous
example could raise havoc in the test.  I also don't believe the ":"
is required in the ${@..} construct, since the colon merely selects
between testing for $@ set, and $@ "set and non null", and the bug
comes about when $@ is *not* set (such as when there are no
arguments).

My solution also allows for a command name with whitespace in it :-)

Just another shell hacker,
-- 
/== Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ====\
| on contract to Intel, Hillsboro, Oregon, USA                           |
| merlyn@iwarp.intel.com ...!uunet!iwarp.intel.com!merlyn	         |
\== Cute Quote: "Welcome to Oregon... Home of the California Raisins!" ==/

bob@wyse.wyse.com (Bob McGowen x922-4312 Training) (07/25/89)

In article <799@jonlab.UUCP> jon@jonlab.UUCP (Jon H. LaBadie) writes:
>In article <2318@wyse.wyse.com>, bob@wyse.wyse.com writes:
>	... Much deleted ...
>> 
---more delted---
>Now a question for Bob.  Can you let me know the need for the
>
>	`whence $0`
>
>construction in your solution.  On inspection, it seems that $0 would
>be sufficient as the script was invoked that way to begin with.  But
>I am certain I am overlooking some subtle situation in which that
>is not the case.
>-- 
>Jon LaBadie
>{att, princeton, bcr}!jonlab!jon
>{att, attmail, bcr}!auxnj!jon

In my experience, when you invoke the Bourne shell on the command line
to run a script, the PATH is not consulted.  For example, _cx_, which
is in my personnal bin, which is part of my path, will generate the
error:
	cx: cx: cannot open
when I run it via:
	sh cx

I wanted to generate the absolute pathname of the script so I could
give it to sh to run, thereby guaranteeing(sp?) that sh would find
the script.

This may of course not be the case with all versions of sh.  My
experience is with XENIX and SUN versions (I can get at these
right now:-)

Bob McGowan  (standard disclaimer, these are my own ...)
Customer Education, Wyse Technology, San Jose, CA
..!uunet!wyse!bob
bob@wyse.com

kdb@chinet.chi.il.us (Karl Botts) (07/25/89)

In article <1792@cadillac.CAD.MCC.COM> ned%cad@MCC.COM (CME Ned Nowotny) writes:
>In article <14463@bfmny0.UUCP> tneff@bfmny0.UUCP (Tom Neff) writes:
>>My thanks to all who have responded via mail or news posting to my
>>question about forcing the Korn shell (KSH) to run a script using the
>>Bourne shell (SH) instead of itself.  There have been no completely
>>satisfactory answers, but I will summarize the State Of What Is Known.

Well, let me take a shot: put the following line as the first of any script
you want to run only under ksh, not sh:

2>/dev/null PPID=0 && { echo "$0: korn shell script" 1>&2; exit 1; }

It's awful to look at, but it works.  It's worse to type, and I haven't
figured out any way to put it in a variable or alias or function or
anything.

The rational is this:  using any of the built-in commands which are in ksh
but not sh to detect the difference can never be bulletproof, because a
binary (or whatever) can always be created with the same name as the ksh
built-in, which will do something (who knows what) under sh.  In fact, it
is not a particularly remote possibility that this could happen, because
writing binaries to emulate ksh built-ins is a perfectly reasonable thing
to do.

In fact, there is nothing that ksh can do that sh can't which can be
a bulletproof test, because ksh is supposed to be a superset of sh, and
even though it isn't it is legitimate for future versions to be closer to
being a superset--therefore any failures of super-setness used to make a
test are liable to go away.

What is needed is something which sh can do that ksh can't.  So far as I
can tell by empiric methods there is only one such thing--assign to the
PPID environment variable.  (ksh can assign to RANDOM, PWD and all the
others I tested.)  I think it is reasonable to assume that as long as ksh
implements the PPID envar, it cannot afford to permit it to be changed
(since it cannot change its parent.)  In fact, "typeset -r" reveals that
PPID is the only built-in envar with the R/O attribute.

I will be delighted if someone can improve on this miserable kludge.

andrew@root.co.uk (Andrew Dingwall) (08/01/89)

In article <799@jonlab.UUCP> jon@jonlab.UUCP (Jon H. LaBadie) writes:

 	... Much deleted ...

>Combining these comments and using the conditional operator rather than
>an if statement; plus quoting $0 on general principles, we have:
>
>  [ "${RANDOM}" = "${RANDOM}" ] || exec /bin/sh -c "${0}" ${@:+"${@}"}
>

Careful, when you use sh -c "string ...", arguments after the string are
assigned to positional parameters starting with $0 - not $1 as one might expect.

for example, try this:

	$ /bin/sh -c 'echo $@' a b c d
	b c d
	$ /bin/sh -c 'echo $0 $@' a b c d
	a b c d

(this happens on System V (at least)).