manpagez: man pages & more
info gawk
Home | html | info | man

File: gawk.info,  Node: Id Program,  Next: Split Program,  Prev: Egrep Program,  Up: Clones

11.2.3 Printing Out User Information
------------------------------------

The 'id' utility lists a user's real and effective user ID numbers, real
and effective group ID numbers, and the user's group set, if any.  'id'
only prints the effective user ID and group ID if they are different
from the real ones.  If possible, 'id' also supplies the corresponding
user and group names.  The output might look like this:

     $ id
     -| uid=1000(arnold) gid=1000(arnold) groups=1000(arnold),4(adm),7(lp),27(sudo)

   This information is part of what is provided by 'gawk''s 'PROCINFO'
array (*note Built-in Variables::).  However, the 'id' utility provides
a more palatable output than just individual numbers.

   The POSIX version of 'id' takes several options that give you control
over the output's format, such as printing only real ids, or printing
only numbers or only names.  Additionally, you can print the information
for a specific user, instead of that of the current user.

   Here is a version of POSIX 'id' written in 'awk'.  It uses the
'getopt()' library function (*note Getopt Function::), the user database
library functions (*note Passwd Functions::), and the group database
library functions (*note Group Functions::) from *note Library
Functions::.

   The program is moderately straightforward.  All the work is done in
the 'BEGIN' rule.  It starts with explanatory comments, a list of
options, and then a 'usage()' function:

     # id.awk --- implement id in awk
     #
     # Requires user and group library functions and getopt
     # output is:
     # uid=12(foo) euid=34(bar) gid=3(baz) \
     #             egid=5(blat) groups=9(nine),2(two),1(one)

     # Options:
     #   -G Output all group ids as space separated numbers (ruid, euid, groups)
     #   -g Output only the euid as a number
     #   -n Output name instead of the numeric value (with -g/-G/-u)
     #   -r Output ruid/rguid instead of effective id
     #   -u Output only effective user id, as a number

     function usage()
     {
         printf("Usage:\n" \
                "\tid [user]\n" \
                "\tid -G [-n] [user]\n" \
                "\tid -g [-nr] [user]\n" \
                "\tid -u [-nr] [user]\n") > "/dev/stderr"

         exit 1
     }

   The first step is to parse the options using 'getopt()', and to set
various flag variables according to the options given:

     BEGIN {
         # parse args
         while ((c = getopt(ARGC, ARGV, "Ggnru")) != -1) {
             if (c == "G")
                 groupset_only++
             else if (c == "g")
                 egid_only++
             else if (c == "n")
                 names_not_groups++
             else if (c == "r")
                 real_ids_only++
             else if (c == "u")
                 euid_only++
             else
                 usage()
         }

   The next step is to check that no conflicting options were provided.
'-G' and '-r' are mutually exclusive.  It is also not allowed to provide
more than one user name on the command line:

         if (groupset_only && real_ids_only)
             usage()
         else if (ARGC - Optind > 1)
             usage()

   The user and group ID numbers are obtained from 'PROCINFO' for the
current user, or from the user and password databases for a user
supplied on the command line.  In the latter case, 'real_ids_only' is
set, since it's not possible to print information about the effective
user and group IDs:

         if (ARGC - Optind == 0) {
             # gather info for current user
             uid = PROCINFO["uid"]
             euid = PROCINFO["euid"]
             gid = PROCINFO["gid"]
             egid = PROCINFO["egid"]
             for (i = 1; ("group" i) in PROCINFO; i++)
                 groupset[i] = PROCINFO["group" i]
         } else {
             fill_info_for_user(ARGV[ARGC-1])
             real_ids_only++
         }

   The test in the 'for' loop is worth noting.  Any supplementary groups
in the 'PROCINFO' array have the indices '"group1"' through '"groupN"'
for some N (i.e., the total number of supplementary groups).  However,
we don't know in advance how many of these groups there are.

   This loop works by starting at one, concatenating the value with
'"group"', and then using 'in' to see if that value is in the array
(*note Reference to Elements::).  Eventually, 'i' increments past the
last group in the array and the loop exits.

   The loop is also correct if there are _no_ supplementary groups; then
the condition is false the first time it's tested, and the loop body
never executes.

   Now, based on the options, we decide what information to print.  For
'-G' (print just the group set), we then select whether to print names
or numbers.  In either case, when done we exit:

         if (groupset_only) {
             if (names_not_groups) {
                 for (i = 1; i in groupset; i++) {
                     entry = getgrgid(groupset[i])
                     name = get_first_field(entry)
                     printf("%s", name)
                     if ((i + 1) in groupset)
                         printf(" ")
                 }
             } else {
                 for (i = 1; i in groupset; i++) {
                     printf("%u", groupset[i])
                     if ((i + 1) in groupset)
                         printf(" ")
                 }
             }

             print ""    # final newline
             exit 0
         }

   Otherwise, for '-g' (effective group ID only), we check if '-r' was
also provided, in which case we use the real group ID. Then based on
'-n', we decide whether to print names or numbers.  Here too, when done,
we exit:

         else if (egid_only) {
             id = real_ids_only ? gid : egid
             if (names_not_groups) {
                 entry = getgrgid(id)
                 name = get_first_field(entry)
                 printf("%s\n", name)
             } else {
                 printf("%u\n", id)
             }

             exit 0
         }

   The 'get_first_field()' function extracts the group name from the
group database entry for the given group ID.

   Similar processing logic applies to '-u' (effective user ID only),
combined with '-r' and '-n':

         else if (euid_only) {
             id = real_ids_only ? uid : euid
             if (names_not_groups) {
                 entry = getpwuid(id)
                 name = get_first_field(entry)
                 printf("%s\n", name)
             } else {
                 printf("%u\n", id)
             }

             exit 0
         }

   At this point, we haven't exited yet, so we print the regular,
default output, based either on the current user's information, or that
of the user whose name was provided on the command line.  We start with
the real user ID:

         printf("uid=%d", uid)
         pw = getpwuid(uid)
         print_first_field(pw)

   The 'print_first_field()' function prints the user's login name from
the password file entry, surrounded by parentheses.  It is shown soon.
Printing the effective user ID is next:

         if (euid != uid && ! real_ids_only) {
             printf(" euid=%d", euid)
             pw = getpwuid(euid)
             print_first_field(pw)
         }

   Similar logic applies to the real and effective group IDs:

         printf(" gid=%d", gid)
         pw = getgrgid(gid)
         print_first_field(pw)

         if (egid != gid && ! real_ids_only) {
             printf(" egid=%d", egid)
             pw = getgrgid(egid)
             print_first_field(pw)
         }

   Finally, we print the group set and the terminating newline:

         for (i = 1; i in groupset; i++) {
             if (i == 1)
                 printf(" groups=")
             group = groupset[i]
             printf("%d", group)
             pw = getgrgid(group)
             print_first_field(pw)
             if ((i + 1) in groupset)
                 printf(",")
         }

         print ""
     }

   The 'get_first_field()' function extracts the first field from a
password or group file entry for use as a user or group name.  Fields
are separated by ':' characters:

     function get_first_field(str,  a)
     {
         if (str != "") {
             split(str, a, ":")
             return a[1]
         }
     }

   This function is then used by 'print_first_field()' to output the
given name surrounded by parentheses:

     function print_first_field(str)
     {
         first = get_first_field(str)
         printf("(%s)", first)
     }

   These two functions simply isolate out some code that is used
repeatedly, making the whole program shorter and cleaner.  In
particular, moving the check for the empty string into
'get_first_field()' saves several lines of code.

   Finally, 'fill_info_for_user()' fetches user, group, and group set
information for the user named on the command.  The code is fairly
straightforward, merely requiring that we exit if the given user doesn't
exist:

     function fill_info_for_user(user,
                                 pwent, fields, groupnames, grent, groups, i)
     {
         pwent = getpwnam(user)
         if (pwent == "") {
             printf("id: '%s': no such user\n", user) > "/dev/stderr"
             exit 1
         }

         split(pwent, fields, ":")
         uid = fields[3] + 0
         gid = fields[4] + 0

   Getting the group set is a little awkward.  The library routine
'getgruser()' returns a list of group _names_.  These have to be gone
through and turned back into group numbers, so that the rest of the code
will work as expected:

         groupnames = getgruser(user)
         split(groupnames, groups, " ")
         for (i = 1; i in groups; i++) {
             grent = getgrnam(groups[i])
             split(grent, fields, ":")
             groupset[i] = fields[3] + 0
         }
     }

© manpagez.com 2000-2025
Individual documents may contain additional copyright information.