[comp.unix.wizards] awk and shell question

jbayer@ispi.UUCP (Jonathan Bayer) (09/16/89)

HELP!!  I have been pulling my hair out over this seemingly simple
problem:

I have to get the user id, and then get the user description from /etc/passwd.

Now, getting the user id was easy.  The script is below:


LOGNAME=`who am i | awk '{
	logname=$1
	print logname
	}'`


LOGNAME is set nicely here.  However, when I try to do something similar
to /etc/passwd as follows:
a=" awk -F: '\$1 == \"$LOGNAME\" {
	user=\$5;
	print user
	}'"
USER=`cat /etc/passwd | $a`


I get the following error:


awk: syntax error at source line 1
 context is
	 >>> ' <<< 
awk: bailing out at source line 1



Now, the following line does work (when I hardcode in LOGNAME):


USER=`cat /etc/passwd | awk -F: '$1 == "root" { user=$5; print user }'`



So my question is, what am I doing wrong here?  I am not that fluent in awk,
so this doesn't make too much sense right now.

This is running on an SCO Xenix 386, 2.3.3 system.


JB
-- 
Jonathan Bayer		Intelligent Software Products, Inc.
(201) 245-5922		500 Oakwood Ave.
jbayer@ispi.COM		Roselle Park, NJ   07204    

karish@forel.stanford.edu (Chuck Karish) (09/17/89)

In article <1163@ispi.UUCP> jbayer@ispi.UUCP (Jonathan Bayer) wrote:
<LOGNAME=`who am i | awk '{
<	logname=$1
<	print logname
<	}'`
<
<a=" awk -F: '\$1 == \"$LOGNAME\" {
<	user=\$5;
<	print user
<	}'"
<USER=`cat /etc/passwd | $a`

USER=`cat /etc/passwd | eval $a`

	Chuck Karish		karish@mindcraft.com
	(415) 493-9000		karish@forel.stanford.edu

hinton@netcom.UUCP (Greg Hinton) (09/18/89)

In article <1163@ispi.UUCP> jbayer@ispi.UUCP (Jonathan Bayer) writes:
> [ . . . . ]
>I have to get the user id, and then get the user description from /etc/passwd.
> [ . . . . ]

How about this:
grep \^`who am i | cut -d' ' -f1`\: /etc/passwd | cut -d: -f5

-- 
Greg Hinton
DOMAIN: hinton@netcom.uucp
UUCP: uunet!apple!netcom!hinton

ok@cs.mu.oz.au (Richard O'Keefe) (09/18/89)

In article <2412@netcom.UUCP>, hinton@netcom.UUCP (Greg Hinton) writes:
: In article <1163@ispi.UUCP> jbayer@ispi.UUCP (Jonathan Bayer) writes:
: >I have to get the user id, and then get the user description from /etc/passwd.

: How about this:
: grep \^`who am i | cut -d' ' -f1`\: /etc/passwd | cut -d: -f5

Watch out for BSD-derived systems, where the first "word" from the
"who am i" command has the form "machine!user", e.g. munmurra!ok.
You want the bit after the !.  A much simpler method is to use a shell
variable.  Check "environ" in the manual, and you'll find that you have
	LOGNAME		# System V
or	USER		# BSD
or both.  Just do
	grep "^${LOGNAME}:" /etc/passwd | what-ever-you-want-here
Again, watch out for Yellow Pages; if you're using that you have to do
	ypcat passwd | grep "^${LOGNAME}" | what-ever-you-want-here

guy@auspex.auspex.com (Guy Harris) (09/20/89)

>>: >I have to get the user id, and then get the user description from
>>: >/etc/passwd.
>>Again, watch out for Yellow Pages; if you're using that you have to do...

Or, if you want to do something that's independent of the details of how
password file access is implemented, try the attached commands.
"getpwnam" takes one argument, a user name, and prints its password
file entry; "getpwuid" takes one argument, a user ID number, and prints
its password file entry.

This should work fine, and use the most efficient access form available,
if you have:

	a traditional text file "/etc/passwd";

	a "dbm" database, 4.3BSD-style;

	a Yellow Pages map, or some other network server-based map
	   (e.g., Hesiod, if the "optional" implementations of "getpwnam()"
	   and its "inverse counterpart" are present; does the Apollo
	   Registry implementation have "getpwnam()" and "getpwuid()"
	   routines that query the registry?);

	or anything else that has "getpwnam()" and "getpwuid()"
	    routines.

and if your system actually implements the most efficient access form
available in "getpwnam()" and "getpwuid()".

With any luck, these should run on anything V7 or later.  If you have
something later, you can consider other improvements, such as using
"strtol" instead of "atoi" to convert the user ID, and catch non-numeric
strings, if your system has "strtol".

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  getpwnam.c getpwuid.c
# Wrapped by guy@auspex on Tue Sep 19 11:28:42 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'getpwnam.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'getpwnam.c'\"
else
echo shar: Extracting \"'getpwnam.c'\" \(523 characters\)
sed "s/^X//" >'getpwnam.c' <<'END_OF_FILE'
X#include <stdio.h>
X
X#include <pwd.h>
X
Xextern struct passwd *getpwnam();
X
Xint
Xmain(argc, argv)
X	int argc;
X	char **argv;
X{
X	register struct passwd *pw;
X
X	if (argc != 2) {
X		fprintf(stderr, "Usage: getpwnam <username>\n");
X		exit(1);
X	}
X	if ((pw = getpwnam(argv[1])) == NULL) {
X		fprintf(stderr, "getpwnam: No such user \"%s\"\n",
X		    argv[1]);
X		exit(2);
X	}
X	printf("%s:%s:%d:%d:%s:%s:%s\n",
X	    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
X	    pw->pw_gecos, pw->pw_dir, pw->pw_shell);
X	exit(0);
X	/*NOTREACHED*/
X}
END_OF_FILE
if test 523 -ne `wc -c <'getpwnam.c'`; then
    echo shar: \"'getpwnam.c'\" unpacked with wrong size!
fi
# end of 'getpwnam.c'
fi
if test -f 'getpwuid.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'getpwuid.c'\"
else
echo shar: Extracting \"'getpwuid.c'\" \(528 characters\)
sed "s/^X//" >'getpwuid.c' <<'END_OF_FILE'
X#include <stdio.h>
X
X#include <pwd.h>
X
Xextern struct passwd *getpwuid();
X
Xint
Xmain(argc, argv)
X	int argc;
X	char **argv;
X{
X	register struct passwd *pw;
X
X	if (argc != 2) {
X		fprintf(stderr, "Usage: getpwuid <user id>\n");
X		exit(1);
X	}
X	if ((pw = getpwuid(atoi(argv[1]))) == NULL) {
X		fprintf(stderr, "getpwuid: No such user \"%s\"\n",
X		    argv[1]);
X		exit(2);
X	}
X	printf("%s:%s:%d:%d:%s:%s:%s\n",
X	    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
X	    pw->pw_gecos, pw->pw_dir, pw->pw_shell);
X	exit(0);
X	/*NOTREACHED*/
X}
END_OF_FILE
if test 528 -ne `wc -c <'getpwuid.c'`; then
    echo shar: \"'getpwuid.c'\" unpacked with wrong size!
fi
# end of 'getpwuid.c'
fi
echo shar: End of shell archive.
exit 0

bob@omni.com (Bob Weissman) (09/20/89)

In article <2130@munnari.oz.au>, ok@cs.mu.oz.au (Richard O'Keefe) writes:
> Again, watch out for Yellow Pages; if you're using that you have to do
> 	ypcat passwd | grep "^${LOGNAME}" | what-ever-you-want-here

I hate to see this kind of inefficiency advertised.  Use
	ypmatch $LOGNAME passwd | whatever
instead.

-- 
Bob Weissman      <bob@omni.com>
UUCP:             ...!{apple,pyramid,sgi,tekbspa,uunet}!koosh!bob
Hofstadter's Law: It always takes longer than you expect,
                  even when you take into account Hofstadter's Law.

wesommer@athena.mit.edu (William Sommerfeld) (09/20/89)

In article <2471@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes:

   Does the Apollo Registry implementation have "getpwnam()" and "getpwuid()"
   routines that query the registry?

Yes, it does.

						
--

gs26@prism.gatech.EDU (Glenn R. Stone) (09/20/89)

In article <2130@munnari.oz.au> ok@cs.mu.oz.au (Richard O'Keefe) writes:
>In article <2412@netcom.UUCP>, hinton@netcom.UUCP (Greg Hinton) writes:
>: In article <1163@ispi.UUCP> jbayer@ispi.UUCP (Jonathan Bayer) writes:
>: >I have to get the user id, and then get the user description from 
>: >/etc/passwd.
>Again, watch out for Yellow Pages; if you're using that you have to do
>	ypcat passwd | grep "^${LOGNAME}" | what-ever-you-want-here

Ummm.... I don't think this is the preferred method, especially when
yp is dbm-ized... you REALLY don't want do do this if you have a 
yp like this one, with yppasswd something on the order of the lower
half of five figures left of the decimal.  Better is

ypmatch $LOGNAME passwd | yourprog

which lets yp do what it's supposed to, namely, look up a passwd entry
quickly and efficiently, no muss, no fuss, you'll be home in time for dinner.

Glenn R. Stone
gs26@prism.gatech.edu, CCASTGS@GITNVE2.BITNET, ...!gatech!gitpyr!ccastgs
Box 30372, Atlanta, GA 30332
"We know it's impossible, now how's the best way to do it?"

clewis@eci386.uucp (Chris Lewis) (09/21/89)

In article <1163@ispi.UUCP> jbayer@ispi.UUCP (Jonathan Bayer) writes:
>
>HELP!!  I have been pulling my hair out over this seemingly simple
>problem:

>a=" awk -F: '\$1 == \"$LOGNAME\" {
>	user=\$5;
>	print user
>	}'"
>USER=`cat /etc/passwd | $a`

>I get the following error:

Various ugly syntax errors.

Several people have given varyingly ugly solutions using other utilities,
evals and so on.

Your problem is how to get parameters into an awk program.  

The other solutions probably work, but I'm posting because of the general
applicability of the technique I'm going to demonstrate here, not so much 
the specific example (I normally use sed for picking apart /etc/passwd)

This technique is in some of the AT&T UNIX V3 UNIX documentation.  And 
appears in many well written awk programs that have been published.  
I use it *very* extensively (10K+ awk scripts in production code):

a=`awk -F: '$1 == "'$LOGNAME'" { print $5}'`

Effectively what you want to do is enclose your script in single quotes,
and surround any parameter you want to get in with single quotes again.
The back-quoted command line is actually composed of *three* separate
strings:

	1) 	'$1 == "'
	2)	$LOGNAME
	3)	'" { print $5}'

Only the second one is actually eval'd, the 1st and 2nd one aren't, the
result is concatenated together then run as a command line.

voila.

T'other nice thing is that you don't go completely crazy trying to escape
all of the shell metacharacters in the non-parameterized part of the awk
program.  Sooo, the awk script still looks pretty normal.  In more 
substantial scripts it makes sense to use awk variables:

a=`awk 'BEGIN {
		var1 = "'$var1'"
		var2 = "'$var2'"
	    ....

Actually, even tho it's kinda ugly, the $variable should also be enclosed
in double quotes, ala:

a=`awk -F: '$1 == "'"$LOGNAME"'" { print $5}'`

Prevents you from being shot in the head by variables containing spaces
and (most) other shell metacharacters.

Disadvantage: introducing single quotes into the awk script is wierd...
eg:

	a=`awk '{ printf "'"'"'" }'`
-- 
Chris Lewis, R.H. Lathwell & Associates: Elegant Communications Inc.
UUCP: {uunet!mnetor, utcsri!utzoo}!lsuc!eci386!clewis
Phone: (416)-595-5425

gwc@root.co.uk (Geoff Clare) (09/23/89)

In article <1989Sep20.210951.10759@eci386.uucp> clewis@eci386.UUCP (Chris Lewis) writes:
>In article <1163@ispi.UUCP> jbayer@ispi.UUCP (Jonathan Bayer) writes:
>>
>>HELP!!  I have been pulling my hair out over this seemingly simple
>>problem:

[ awk script deleted ]

>Your problem is how to get parameters into an awk program.  
>
>The other solutions probably work, but I'm posting because of the general
>applicability of the technique I'm going to demonstrate here, not so much 
>the specific example (I normally use sed for picking apart /etc/passwd)
>
>This technique is in some of the AT&T UNIX V3 UNIX documentation.  And 
>appears in many well written awk programs that have been published.  
>I use it *very* extensively (10K+ awk scripts in production code):
>
>a=`awk -F: '$1 == "'$LOGNAME'" { print $5}'`
                                            ^ /etc/passwd missing

I find it much more readable, and less prone to error, to assign the
necessary awk variables on the command line:

a=`awk -F: '$1 == LOGNAME { print $5 }' LOGNAME="$LOGNAME" /etc/passwd`

If the awk program is very long, this also means you can put it
in a file for use with '-f', and still be able to set dynamic
values for use in the program.

The only problem with this method is if you want awk to read stdin.
Most implementations incorrectly only read stdin if there are no
arguments at all, not if there are no file arguments.  However you can
get round this by giving a file name of "-".
-- 
Geoff Clare, UniSoft Limited, Saunderson House, Hayne Street, London EC1A 9HH
gwc@root.co.uk  (Dumb mailers: ...!uunet!root.co.uk!gwc)  Tel: +44-1-315-6600

larry@macom1.UUCP (Larry Taborek) (09/25/89)

From article <1163@ispi.UUCP>, by jbayer@ispi.UUCP (Jonathan Bayer):
> 
> HELP!!  I have been pulling my hair out over this seemingly simple
> problem:
> 
> LOGNAME is set nicely here.  However, when I try to do something similar
> to /etc/passwd as follows:
> a=" awk -F: '\$1 == \"$LOGNAME\" {
> 	user=\$5;
> 	print user
> 	}'"
> USER=`cat /etc/passwd | $a`
> 
> I get the following error:
> 
> awk: syntax error at source line 1
>  context is
> 	 >>> ' <<< 
> awk: bailing out at source line 1
> Now, the following line does work (when I hardcode in LOGNAME):
> USER=`cat /etc/passwd | awk -F: '$1 == "root" { user=$5; print user }'`

> Jonathan Bayer		Intelligent Software Products, Inc.
> (201) 245-5922		500 Oakwood Ave.
> jbayer@ispi.COM		Roselle Park, NJ   07204    

Jonathan,

try this instead:

USER=`cat /etc/passwd | awk -F: '{printf("%-8.8s %-20.20s\n",$1,$5)}'`
echo $USER

this works, but I think what you really want is:

cat /etc/passwd | awk -F: '{printf("%-8.8s %-20.20s\n",$1,$5)}'

without the USER=`

the awk script basically prints the 1st and 5th fields in a printf
statement, where the contents of $1 are put in a field 8 characters
long left justified, and the contents of $5 are put into a field
20 characters long left justified.  The \n should be a line feed.

Hope this helps...

Larry
-- 
Larry Taborek	..!uunet!grebyn!macom1!larry	Centel Federal Systems
		larry@macom1.UUCP		11400 Commerce Park Drive
						Reston, VA 22091-1506
						703-758-7000

maart@cs.vu.nl (Maarten Litmaath) (09/27/89)

gs@diab.se (Greger Sernemar) writes:
\...
\a="`awk -F: '$1 == NAME {
\       user=$5;
\       print user
\       }' NAME=$LOGNAME - `"
\
\The last dash (-) closes standard input i.e enables other program to
\pipe to the awk script.

How can it pipe into awk when awk's stdin is closed?!
A dash doesn't mean CLOSE stdin, it means USE stdin!

\I was forced to either supply a file name or
\a dash to be able to set the awk variable.
\I'm not sure if this is a bug in the version of awk I'm using or not.

This is a bug indeed, present in SunOS 4.0.1 awk too.
Other bugs:

	% awk -F '{print}'
	awk: can't open {print}
	% awk -F
	awk: no argument for -f
	%
-- 
 `I AM NEW HEAR AMD I WANT TO INKRIMENT A |Maarten Litmaath @ VU Amsterdam:
 VURIABLE BY 1 (OONE) IN "c"'  (Tom Neff) |maart@cs.vu.nl, mcvax!botter!maart

dg@lakart.UUCP (David Goodenough) (09/28/89)

gwc@root.co.uk (Geoff Clare) sez:
> clewis@eci386.UUCP (Chris Lewis) writes:
>>This technique is in some of the AT&T UNIX V3 UNIX documentation.  And 
>>appears in many well written awk programs that have been published.  
>>I use it *very* extensively (10K+ awk scripts in production code):
>>
>>a=`awk -F: '$1 == "'$LOGNAME'" { print $5}'`
>                                             ^ /etc/passwd missing
> I find it much more readable, and less prone to error, to assign the
> necessary awk variables on the command line:
> 
> a=`awk -F: '$1 == LOGNAME { print $5 }' LOGNAME="$LOGNAME" /etc/passwd`
> 
> The only problem with this method is if you want awk to read stdin.

Try the following:

awk < file >> otherfile 'BEGIN {
		    np = '`head -1 $datadir/$job`'
		    ml = "'$mailer'"
		    jbu = "'`echo $job | tr a-z A-Z`'"
		    dd = "'$datadir'"
		    ta = "'$targ'"
		 }

rest of awk script follows ....

Lets you get away with murder :-) Note that by judicious use of `` I can
even get the output of a separate shell command into an awk variable:
for example np gets a number from the first line of $datadir/$job
-- 
	dg@lakart.UUCP - David Goodenough		+---+
						IHS	| +-+-+
	....... !harvard!xait!lakart!dg			+-+-+ |
AKA:	dg%lakart.uucp@xait.xerox.com			  +---+

dg@lakart.UUCP (David Goodenough) (09/28/89)

From article <4926@macom1.UUCP>, by larry@macom1.UUCP (Larry Taborek):
> try this instead:
> 
> USER=`cat /etc/passwd | awk -F: '{printf("%-8.8s %-20.20s\n",$1,$5)}'`
> echo $USER
> 
> this works, .....

f::1000:31:Finger Pointer:/:/usr/guest/xfinger

Not on the above it doesn't (think about it).

I suggest:

USER=`cat /etc/passwd | sed -e "s/::/:-:/" | \
			awk -F: '{printf("%-8.8s %-20.20s\n",$1,$5)}'`
-- 
	dg@lakart.UUCP - David Goodenough		+---+
						IHS	| +-+-+
	....... !harvard!xait!lakart!dg			+-+-+ |
AKA:	dg%lakart.uucp@xait.xerox.com			  +---+

ganga@megadata.mega.oz (Ganga Varatharajan) (10/03/89)

From article <4926@macom1.UUCP>, by larry@macom1.UUCP (Larry Taborek):
> 
> USER=`cat /etc/passwd | awk -F: '{printf("%-8.8s %-20.20s\n",$1,$5)}'`
> echo $USER
> 
> this works, but I think what you really want is:
> 
> cat /etc/passwd | awk -F: '{printf("%-8.8s %-20.20s\n",$1,$5)}'
> 
> without the USER=`

I'm trying to alias d (csh) as:
% dirs -l | awk '{ for (i = 1; i < NF+1; i++) { print i-1,$i } }'
to list the directory stack one per line and numbered.

% alias d "dirs -l | awk '{ for (i = 1; i < NF+1; i++) { print i-1,$i } }'"
gives
% $i undefined variable.

% alias d 'dirs -l | awk \'{ for (i=1; i < NF+1; i++) { print i-1,$i}}\' '
gives
% unmatched '

So I use
% alias d "dirs -l | awk -f ~/.dirlist"
which is slow.

Any suggestions?

Thanks
Ganga.

ben@nsf1.mth.msu.edu (Ben Lotto) (10/05/89)

On 3 Oct 89 07:47:42 GMT,
ganga@megadata.mega.oz (Ganga Varatharajan) said:

Ganga> I'm trying to alias d (csh) as:
Ganga> % dirs -l | awk '{ for (i = 1; i < NF+1; i++) { print i-1,$i } }'
Ganga> to list the directory stack one per line and numbered.

Try the following:

alias d "dirs -l | awk '{ for (i = 1; i < NF+1; i++) { print i-1,"'$i'" } }'"

The problem is that any variable is automatically evaluated when
enclosed in double quotes.  So when you get to the variable, end the
double quotes and enclose the variable you want in single quotes.
--

-B. A. Lotto  (ben@nsf1.mth.msu.edu)
Department of Mathematics/Michigan State University/East Lansing, MI  48824