How does #!/bin/sh work ? Why does it sometimes not ?

Blair P. Houghton bhoughto at cmdnfs.intel.com
Sat Oct 13 03:07:33 AEST 1990


In article <7980005 at hpopd.HP.COM> ian at 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?"



More information about the Comp.unix.questions mailing list