[comp.unix.questions] executable awk scripts

whb@vax135.UUCP (Wilson H. Bent) (09/16/87)

[ The line-eater lives!  Probably because people keep feeding it...]

Given that these files are executable, why does this fail:

	#!/bin/awk
	{ print $1 }

when this works:

	#!/bin/sh
	/bin/awk '{ print $1 }'

FYI: tested under two BSD variants (sorry, no SysV):
Sun UNIX 4.2 Release 3.3
Ultrix V2.0-1 System #1: Wed Jul 15 10:28:13 EDT 1987
-- 
Wilson H. Bent, Jr.		... ihnp4!hoh-2!whb
AT&T - Bell Laboratories	(201) 949-1277
Disclaimer: My company has not authorized me to issue a disclaimer.

chris@mimsy.UUCP (09/17/87)

In article <1881@vax135.UUCP> whb@vax135.UUCP (Wilson H. Bent) writes:
>Given that these files are executable [by his 4.2BSD-based test
>systems], why does this fail:
>
>	#!/bin/awk
>	{ print $1 }
>
>when this works:
>
>	#!/bin/sh
>	/bin/awk '{ print $1 }'

The two are not equivalent.  The former runs /bin/awk with argv[1]
set to the path name of the executable file (the awk script) and
argv[2] through argv[n] as the `regular' argument list.  The latter
runs /bin/sh with argv[1] set to the path name of the executable
(the shell script) and argv[2] .. argv[n] as the `regular' list.
Assuming there were no other arguments, this makes argv[2] nil
(or, if you prefer, (char *)0).

In the first version, awk takes this argv[1] and attempts to parse
it as a program: `syntax error near line 1'.  In the second version,
the shell takes this argv[1] and interprets it.  It runs /bin/awk,
this time with argv[1] being the string `{ print $1 }'.

The shell does the proper thing because it assumes that, unless
otherwise specified via flags, an argument names a file that is to
be run.  Awk fails because it assumes that, unless otherwise
specified via flags, an argument is a literal awk program.  Fortunately,
it takes only one flag (`-f') to override this:

	#! /bin/awk -f
	{ print $1 }

Since one is allowed only one argument, this also works:

	#! /bin/awk NR > 1 { print $1 }

except that it does something else entirely.  Here awk's argv[1]
is the string

	`NR > 1 { print $1 }'

which is a valid awk program, and, if there were no other arguments,
would be applied to stdin.  There is at least one other argument:
argv[2] is the name of the awk file.  This makes awk print the first
word of all but the first line of the file---much like `tail +2'.
Awk, however, can read multiple files:

	#! /bin/awk NR > 1 { print }
	Heading that goes at the front of some sort of special
	report format.  Running this executable and naming all the
	files that list the format concatenate this heading and
	the other files.

If this is in a file called `addhead', running `addhead foo bar baz'
prints the four heading lines above, followed by the contents of
file foo, then bar, then baz.  Note that no lines of foo, bar, or
baz are skipped as NR is not reset when awk changes files.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

guy@gorodish.UUCP (09/17/87)

> Given that these files are executable, why does this fail:
> 
> 	#!/bin/awk
> 	{ print $1 }

Because, if this file is called "foo", and you say "foo", it is as if you had
said "/bin/awk foo".  This tells it to read "foo" as its *input* file, not as
its *program* file, and gives it no program; since it had no program, it issues
its all-purpose error message "awk: syntax error near line N" followed by "awk:
bailing out near line N".

A "#!" script is run by taking the argument list to the original "exec",
shifting it right by one, sticking in the name of the script at the left and,
if an argument appeared on the "#!" line, shifting the list right one more and
sticking that argument in on the front.  Thus, if "foo" contained

	#! /bin/awk -f
	{ print $1 }

saying "foo" would run "/bin/awk -f foo", which is what you want.

> FYI: tested under two BSD variants (sorry, no SysV):
> Sun UNIX 4.2 Release 3.3
> Ultrix V2.0-1 System #1: Wed Jul 15 10:28:13 EDT 1987

Most System V systems have probably not picked up "#!" - none of AT&T's have,
as far as I know - so it would have been hard to test it there.  Note, though,
that the SunOS 3.3 "awk" is a bug-fixed version of the S5R2 "awk" (which is
noticeably faster than the "awk" that comes with 4BSD, which is somewhere
between the V7 and S5R2 one along the "awk" timeline).
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

ballou@brahms.Berkeley.EDU.UUCP (09/18/87)

In article <1881@vax135.UUCP> whb@vax135.UUCP (Wilson H. Bent) writes:
>[ The line-eater lives!  Probably because people keep feeding it...]
>
>Given that these files are executable, why does this fail:
>
>	#!/bin/awk
>	{ print $1 }
>
>when this works:
>
>	#!/bin/sh
>	/bin/awk '{ print $1 }'
>

The semantics of '#!' are as follows.  A command line is constructed by taking
the text immediately following the #! (up to some limit which, I believe, is
30 characters), appending a space, then copying the command line which invoked
the shell script.  So, assuming in both cases the shell script file name is
'foo',  the first case behaves as:

	/bin/awk foo

while the second case behaves as:

	/bin/sh /bin/awk '{ print $1 }'

In the second case, the shell forks a process with command line

	/bin/awk '{ print $1 }'

The first case leaves awk with no program (since a file containing a program
must be specified as -f filename).



--------
Kenneth R. Ballou			ARPA:  ballou@brahms.berkeley.edu
Department of Mathematics		UUCP:  ...!ucbvax!brahms!ballou
University of California