[comp.unix.questions] How does #!/bin/sh work ? Why does it sometimes not ?

ian@hpopd.HP.COM (Ian Watson) (10/11/90)

I've seen loads of shell scripts start with 
#!/bin/sh
as the first line.

I understand that the C shell sees this and knows to execute it as a Bourne
shell script.  I've tried this on both HP-UX 7.0 (which works as I expect) and
SCO Unix V.3.2 (which doesn't).  I can't seem to get SCO's csh to run the
script through the Bourne shell.  Is this feature of the C shell standard
Unix, and am I doing something wrong ?

Why can't I find this feature mentioned in TFM for either HP-UX or SCO Unix ?
Am I just not looking hard enough / in the right place ?

Also, on the HP-UX, I notice that

$ csh script
and
$ csh < script
run the script in the C shell, but
$ csh
followed by
% script
does what I want (runs script through Bourne shell).  Can anyone explain under
what circumstances the #!/bin/sh line is or is not honoured by the C shell ?

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ian Watson, HP Pinewood Information Systems Division, England.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Phone :				(Intl)+44 344 763015
Unix mail (Internet) :		ian@hpopd.HP.COM
Unix mail (UUCP) :		...!hplabs!hpopd!ian
Openmail :			ian watson/pinewood,lab,hpopd
Openmail from Unix :		ian_watson/pinewood_lab_hpopd@hpopd
HPDesk :			Ian WATSON/HP1600
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/12/90)

In article <7980005@hpopd.HP.COM> ian@hpopd.HP.COM (Ian Watson) writes:
>#!/bin/sh

On systems that support this, the kernel takes care of it as indicated
under EXEC(2) in the reference manual.

>I understand that the C shell sees this ...

Normal shells first try to exec() the file, and if that fails due to
unknown executable image format then MIGHT peek at the FIRST character
to determine whether or not a Bourne shell or C-shell should be used
to interpret the file.  The C-shell often is set up to decide that an
initial '#' character indicates the file is a C-shell script, but of
course that is not a reliable test.  Other shells simply always use a
Bourne shell, which is what I recommend.  It is possible that somebody
has hacked their shells to try to apply full #!-style decoding in lieu
of having support for it in exec(), but that would be pretty awful.

bhoughto@cmdnfs.intel.com (Blair P. Houghton) (10/13/90)

In article <7980005@hpopd.HP.COM> ian@hpopd.HP.COM (Ian Watson) writes:
>I've seen loads of shell scripts start with 
>#!/bin/sh
>as the first line.
>
>I understand that the C shell sees this and knows to execute it as a Bourne

Not the C shell.  The kernel.  When you type the script name,
the shell first checks to see if it's a builtin command
(like 'cd'), if not, it checks in the directories in $path
and execs the first matching filename.  It uses the execve(2)
call.

execve(2) opens the file and sees '#!' as the first two
bytes.  The first two bytes of an executable are the "magic
number."  They tell execve(2) what type of executable the
file contains (do 'man magic' to see examples; these are
used by the file(1) command to see what sort of file you've
given it, and may not match exactly with execve(2)'s
interpretation).

When execve(2) sees '#!' (hex 2321) it interprets the rest
of the line as the pathname to an interpreter, and its
arguments; it opens _that_ file as an executable, and pipes
it the balance of the first file as input.

Other things you can do with it are

	#! nroff -man

	#! cat

	#! more

Basically, any program that reads stdin for input can be run
this way.  (Be careful, though, some commands _seem_ to read
stdin when they've really opened /dev/tty and are reading
your typing raw -- passwd(1) does this).

>Why can't I find this feature mentioned in TFM for either HP-UX or SCO Unix ?
>Am I just not looking hard enough / in the right place ?

Look in execve(2), and in the deeper part of csh(1).
sh(1) doesn't say a word (at least under Ultrix), primarily
because it does not care what the first line
of the file looks like.  It passes the script to execve(2)
and if execve(2) sees the '#!' it forks a subshell which
just gets the file's contents.  Csh(1) _will_, however,
_after_ giving execve(2) a shot, fork a sh(1) iff the
first line of the file is _not_ a comment.  I.e., if you
do not put a comment or a '#!' as the first line, the
script defaults to being a sh-script.

>Also, on the HP-UX, I notice that
>
>$ csh script

What you've done here is executed 'csh' to open 'script' and read
its contents as input (to source it, albeit in a subshell of the
one you're typing into); what you have not done is that you have not
executed 'script'.

>and
>$ csh < script

Here you've done the same thing, except the script comes
via standard-input rather than another file descriptor that
the sub-shell would open (standard-input, -output, and -error
are open by default).

>run the script in the C shell, but
>$ csh

Now you've just started a subshell.

>followed by
>% script

Here you've executed the script itself.  Csh will go through the
routine outlined here:

	csh recognizes it's not builtin
	csh builds the pathname by looking through $path members
	csh fork(2)-and-execve(2)'s the script
	execve(2) opens the file and looks for the 'magic number'
	if the magic number is '#!',
		execve runs the rest of the line as a command
		and pipes it the rest of the file as standard input
		and then exits
	else if the magic number denotes a binary-executable,
		execve runs the binary and then exits
	else
		execve returns (normally it doesn't, it just exits,
		which is why the fork(2) is used, also) with
		a status in errno(2) that says "it's not my job, man"
		csh then opens the file:
		if the first line is a comment
			csh reads the rest of the file as commands
			then exits
		else
			csh fork(2)-and-execve(2)'s /bin/sh and pipes
			it the rest of the file as standard input

>does what I want (runs script through Bourne shell).  Can anyone explain under
>what circumstances the #!/bin/sh line is or is not honoured by the C shell ?

To the C shell, that line is a comment; to execve(2), it's
the word of god.

				--Blair
				  "Where's my mitre?"

maart@cs.vu.nl (Maarten Litmaath) (10/13/90)

In article <397@inews.intel.com>,
	bhoughto@cmdnfs.intel.com (Blair P. Houghton) writes:
)...
)When execve(2) sees '#!' (hex 2321) it interprets the rest
)of the line as the pathname to an interpreter, and its
)arguments; it opens _that_ file as an executable, and pipes
)it the balance of the first file as input.

Wrong.  If the file `foo' has `#!/bin/sh' as its first line,
executing `foo' will result in `/bin/sh foo', i.e. the filename
is passed as an _argument_.

If the rest of the file were passed as input to the interpreter,
e.g. the following example would not work:

	#!/bin/sh

	echo 'Give the name:'
	read name
	echo "The name is: $name"

Issuing the command `sh < foo' would have the same unwanted effects.
--
Waiting for this to work: cat /internet/cs.vu.nl/finger/maart

tchrist@convex.COM (Tom Christiansen) (10/13/90)

In article <7980005@hpopd.HP.COM> ian@hpopd.HP.COM (Ian Watson) writes:
>I've seen loads of shell scripts start with 
>#!/bin/sh
>as the first line.
>
>I understand that the C shell sees this and knows to execute it as a Bourne
>shell script.  

On any system I've seen, it's the kernel who recognizes this (often 
in sys/kern_exec.c) and launches the proper interpreter.  

I happen to believe that doing anything else than this is undesirable --
currently either csh or sh may end up being called (on a BSD system) if
the program doesn't have either the magic number of compiled a.out or an
interpreter line, depending on the first character (# or not) of the file.

--tom
--
 "UNIX was never designed to keep people from doing stupid things, because 
  that policy would also keep them from doing clever things." [Doug Gwyn]