Parsing “id” strings in a Shell Script?

Hello Dave. I need a Bash shell script that creates a directories with the group names automatically when user logs in to the system, in their home directory. For example, user “sandy” might have the following set of groups from the ‘id’ command: uid=10002(sandy) gid=119(rtkit) groups=119(rtkit),10001(admin),10003(pr007drdl) and the directories “rtkit”, “admin” and “pr007drdl” should then be created. How do I do it?

I think this falls into the category of “Dave, can you do my homework?” but since it’s an interesting problem, I’ll spend some time talking about how to do this sort of simple parsing and then wrap it with with a ‘for’ loop that steps through each value.
There are also nuances to your assignment that are important. For example, trying to create a directory that already exists is sloppy and poor coding, so part of what you need to do is test for the existence of the group subdirectory prior to requesting its creation. Sure you can use a “force” flag or an “ignore error” flag on the call to ‘mkdir’, but that’s cheap coding and if we’re going to write something, let’s write it well, right?
So here’s the basic approach I’m envisioning: in the .login file extract one or more group names from the output of the ‘id’ command. For each value test to see if it exists yet as a subdirectory. If it does, move to the next value. If it doesn’t, create it.
Let’s see how that looks as a script.
The first step is to break down the ‘id’ string into the individual group elements. If you look at the output of “id”, you’ll see that there’s a regular pattern to the results, so it’s not too bad. Here’s the output of my own id string:

uid=501(taylor) gid=20(staff) groups=20(staff),401(com.apple.access_screensharing),404(com.apple.sharepoint.group.3), 402(com.apple.sharepoint.group.1),403(com.apple.sharepoint.group.2),12(everyone),33(_appstore),61(localaccounts),
79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),100(_lpoperator),204(_developer)

Kind of crazy complicated, but it’s still three fields separated by spaces, the third of which is of the form groups=NUMBER(name),NUMBER(name), etc. So my approach to this is to first jettison the uid and gid values. That looks like this:

id | cut -d\ -f3

The ‘-d’ flag to cut specifies the delimiter that separates fields, then ‘-f3’ says we’re only interested in the third field, the group list.
When run, it produces something like this:

groups=20(staff),401(com.apple.access_screensharing),404(com.apple.sharepoint.group.3), 402(com.apple.sharepoint.group.1),403(com.apple.sharepoint.group.2),12(everyone),33(_appstore),61(localaccounts),
79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),100(_lpoperator),204(_developer)

To break it up further, I’m going to use ‘tr’ to translate all open parens into carriage returns, essentially turning this one long line into a lot of very short lines:

$ id | cut -d\ -f3 | tr ‘(‘ ‘\n’
groups=20
staff),401
com.apple.access_screensharing),404
com.apple.sharepoint.group.3),402
com.apple.sharepoint.group.1),403
com.apple.sharepoint.group.2),12
everyone),33
_appstore),61
localaccounts),79
_appserverusr),80
admin),81
_appserveradm),98
_lpadmin),100
_lpoperator),204
_developer)

Two things left: we need to remove everything after the closing paren (which we can do with another invocation of the ‘cut’ command) and we need to remove the groups=20 line at the very beginning. String it all together and here’s what we have:

$ id | cut -d\ -f3 | tr ‘(‘ ‘\n’ | cut -d\) -f1 | grep -v ‘=’
staff
com.apple.access_screensharing
com.apple.sharepoint.group.3
com.apple.sharepoint.group.1
com.apple.sharepoint.group.2
everyone
_appstore
localaccounts
_appserverusr
admin
_appserveradm
_lpadmin
_lpoperator
_developer

With that done, the rest is a breeze, we just wrap it with a ‘for’ loop:

for groupname in $(id | cut -d\ -f3 | tr ‘(‘ ‘\n’ | cut -d\) -f1 | grep -v ‘=’ )
do
  if [ ! -d $groupname ] ; then
    mkdir $groupname
  fi
done

That’s your code snippet. In my case, it’d be easy to add a filter that would, for example, skip any groupname that started with an underscore or contained a dot, but I’ll leave that as an exercise for the reader.

Leave a Comment

Receive My Weekly Email Newsletter:

Your email address:*
First Name
Please enter all required fields Click to hide
Correct invalid entries Click to hide

Recent Posts

On My YouTube Channel

Date Archives