How to find group membership in OS X

AccountsIconXGroups in OS X are special account entries that act as umbrellas under which user accounts may exist, allowing single adjustments of access permissions to immediately apply to numerous users. As a result, the use of groups when setting up a multi-user Mac can be exceptionally useful, but then again may also leave open security holes if not done correctly.

Since groups are technically user account entries in the system’s directory that hold information about other user accounts, groups can be members of other groups, and thus form hierarchies. As a result, there are two types of groups that a user account is associated with in OS X. The first are those that are explicitly defined for a user, and the second are those where the user is given inferred membership based on the hierarchy of the group organization.

Displaying explicit groups

The main directory management utility in OS X is the “dscl” command, which can be used to search for the explicitly assigned groups for users in a couple of ways:

Within the dscl utility:

  1. Open the terminal and enter “dscl”
  2. At the prompt, enter the following command, changing USERNAME accordingly
    search /Local/Default/Groups GroupMembership USERNAME

At the Terminal prompt:

  1. Open the Terminal and enter the following command:
    dscl . search /Groups GroupMembership USERNAME

Both of these approaches have an inherent frustration, in that they will search for an display these attributes on a per-user basis, so the output is a long list of the groups, under which you will see all attributes of the GroupMembership tag (ie, all users for each group).

To make this more legible, you can make use of a few Terminal tricks, by piping this output to other commands to parse and modify it so we only get the information we want. In the following command, we use the vertical-line “pipe” character to send the output to the “grep” filtering utility, which will only print lines with “=” in them. This will result in a list of the groups, but also have “GroupMembership = (” listed in each line. Since these are always in order with the group name first, we can use another text-handling command “awk” to only print out the first of these ordered items:

dscl . search /Groups GroupMembership USERNAME | grep = | awk '{print $1}'

Finally if you know a specific group by name and only wish to print out the current members of that group, the following command will do this. In this case, we are targeting the default “staff” group, but can switch this name to that of any other group name on the system:

dscl . read /Groups/staff GroupMembership

Displaying effective groups:

The effective groups include all under which the current user can operate, as opposed to explicit ones that the user has been specifically assigned to. For instance, all users may be affected by the “everyone” group, where if your username or member groups are not associated with a file, then the “everyone” group will take over as a fallback for determining access eligibility for the file. To see a list of effective groups, you can use any of the following Terminal commands:

groups USERNAME
id -Gn USERNAME
id -p

View only inferred groups:

Since we have both explicit groups and effective groups, the difference between these is the inferred groups for a user. This is all of those listed with the “groups” or “id” commands, with the explicit groups mentioned in the “dscl” command removed. You can do this manually for the most part, but can also assemble a command such as the following to do the same thing:

comm -23 <(id -Gn USERNAME | sed 's/ /\n/g' | sort) <(dscl . search /Groups GroupMembership USERNAME | grep = | awk '{print $1}' | sort)

Explaining this command:

This “comm” command compares two files line-by-line and with the “-23” flag will output the items listed that are not common between the files. However, we are working with the output of secondary commands instead of files, and one of these commands outputs a line-by-line list whereas the other outputs a series of items in-line.

This setup makes it difficult to make our comparison, but we can do it using some Terminal tricks to standardize the output of the two separate commands, and then have the Terminal treat these outputs as files for the “comm” command to work on. Here is how this is done:

For the “id” command, we are going to convert the serial output to a line-by-line list using piping (chaining of commands) to the “sed” string editor command. This string editor command is a rather complex tool, but in this case we are using the expression “s/ /\n/g” to substitute a space with a new-line character for all lines in the output of the previous command:

id -Gn USERNAME | sed 's/ /\n/g'

Now we are going to pipe the output of this list to the “sort” command which will sort the list alphabetically.

id -Gn USERNAME | sed 's/ /\n/g' | sort

Now we look at the second command, which by default outputs a series of arrays for group membership for the specified user:

dscl . search /Groups GroupMembership USERNAME

As with the first command, we need to modify this output, which we first do with the “grep” command to filter the output to only include lines with an equals symbol, since the group names in the output share this symbol uniquely:

dscl . search /Groups GroupMembership USERNAME | grep =

Finally, the output now is a series of lines that begin with the desired group names, but then have additional components, such as the equals sign we used to isolate these lines with the “grep” command. Luckily the output is stereotyped so the desired group names are the first word on the lines, which means we can use another text-handling command “awk” to isolate this word from the rest of the line:

dscl . search /Groups GroupMembership USERNAME | grep = | awk '{print $1}'

In this case, the “awk ‘{print $1}'” command prints out just the first word for each line piped to it, resulting in an output of just the group names.

As a final step, we will sort this output as we did with the “id” command output:

dscl . search /Groups GroupMembership USERNAME | grep = | awk '{print $1}' | sort

All of these approaches output “standard output” to the Terminal window, which is why you see it all printed immediately below the command when you execute it. However, for the “comm” command we need to provide this input a file. We can do this with the “<()” file redirection syntax in the Terminal, which takes any standard output and treats it as a file input for a command, such as the following example:

comm <(command1) <(command2)

In this case, the output of command1 and command2 are treated as files for the “comm” command, so we simply substitute these with the two piping commands we assembled above:

comm -23 <(id -Gn USERNAME | sed 's/ /\n/g' | sort) <(dscl . search /Groups GroupMembership USERNAME | grep = | awk '{print $1}' | sort)

In this respect, the “comm” command will act on these two virtual “files,” and we simply add the “-23” flag for the “comm” command to change its default behavior from showing common lines for the files, to only showing differences found, which in this case will take the “id” command’s output and remove items that are common with the “dscl” command output. Since the groups listed by the “dscl” command is inherently a subset of those listed by the “id” command, and since the “dscl” command shows explicitly-defined groups only, we now have a way to isolate the inherently-defined groups.

4 thoughts on “How to find group membership in OS X

  1. bradmacpro

    And what’s wrong with using the Directory Utility found in the /System/Library/CoreServices/Applications/

    1. Topher Kessler Post author

      Its a nuance limitation, but you cannot view a list of the groups to which a user belongs in Directory Utility. You can view the users of a particular group, but there is no entry for a user that shows the relevant groups for that user. In the OS X directory, the group points to the user, and not vice-versa, so this information is organized on the group level. These commands I outlined above simply search for usernames on this level and display instances of groups where the usernames are found. It then offers options for distinguishing between implicit and explicit groups.

  2. Chris Weiss

    What version of dscl was this tested against? I’m not having luck with many of the commands in v10.11 (OS-X Sierra).

Comments are closed.