List Your Group Memberships with groups
Before you blame a permission error on the wrong thing, run groups and see what your shell actually believes about you right now. It also looks up one or more named users, and this walkthrough covers both uses plus the version check, the exit status, and a warning against treating a group listing as proof a running process has already gained a new permission. Allow about ten minutes. The examples use GNU coreutils 9.4, installed here as package version 9.4-3ubuntu6.3.
The route
Jump straight to the step you need, or tick off Done means at the end.
This is a read-only workflow. It does not add users to groups, reload a session, change file ownership or alter a service. You normally do not need sudo.
1. Check the installed command
Confirm which executable the shell will run and which package supplied it:
$ command -v groups
/usr/bin/groups
$ groups --version
groups (GNU coreutils) 9.4
$ dpkg-query -W -f='\${Package} \${Version}\n' coreutils
coreutils 9.4-3ubuntu6.3
The exact path and package revision can differ on another distribution. The installed manual documents the behaviour used here: it accepts optional usernames and lists only --help and --version as options.
2. Inspect the current process
With no username, groups prints the memberships for the current process:
$ groups
users sudo docker alice opsteam direktor
Your own output will contain your own username's groups. The command prints a space-separated list with no label, which is convenient at a prompt but less self-describing in a log.
Checkpoint
Save the result without changing anything, then count the fields if you need a quick sanity check:
$ current_groups=$(groups)
$ printf '%s\n' "$current_groups"
users sudo docker alice opsteam direktor
$ printf 'group count: %s\n' "$(wc -w <<< "$current_groups")"
group count: 6
Do not use the count as an authorisation decision. Group names can be added or removed in the account database while a process keeps using the credentials it inherited when it started.
3. Look up a named user
Pass a username to see the groups recorded for that account. This is useful when checking a service account, an administrator's account or a proposed file-access change:
$ groups root
root : root kvm ollama
$ groups nobody
nobody : nogroup
For a named user, the output includes the username followed by a colon. This is not a test of the groups belonging to your shell; it asks the system's group database about the account you named. You can supply several usernames in one invocation:
$ groups root nobody
root : root kvm ollama
nobody : nogroup
Each requested account gets its own line. Keep the names explicit in scripts so a typo does not silently turn a check into one for a different account.
4. Distinguish account data from live credentials
The manual warns that the no-argument result is for the current process and may differ if the groups database has changed. This matters after an administrator adds a user to a group. First, inspect the account database:
$ groups YOUR_USER
YOUR_USER : YOUR_PRIMARY_GROUP YOUR_ADDITIONAL_GROUP
Replace YOUR_USER with a real account name. This does not update an already running shell. A new login session, or a deliberate credential refresh using your distribution's normal session tools, may be needed before a program receives the new supplementary group.
To check a particular process rather than relying on a shell's state, inspect its status record instead:
$ grep '^Groups:' /proc/$$/status
Groups: 27 100 999
The values in /proc/<pid>/status are numeric group IDs, while groups normally prints names: two different views of the same process credentials. If a permission check is failing, compare the running process with the account lookup before changing file modes or restarting services.
5. Use the exit status in a script
A successful lookup returns status 0. An unknown username returns a non-zero status and a diagnostic:
$ groups definitely-not-a-real-user >/tmp/groups-missing.out 2>/tmp/groups-missing.err
$ printf 'exit status: %s\n' "$?"
exit status: 1
The quotation marks in the diagnostic depend on the locale. Scripts should test the status, not parse the message or assume that a particular group appears in the output:
if groups "$USER" >/tmp/groups-check.out 2>/tmp/groups-check.err; then
printf '%s\n' 'user lookup succeeded'
else
status=$?
printf 'groups lookup failed with status %s\n' "$status" >&2
exit "$status"
fi
The example uses the current account name supplied by the shell. If you substitute an external value, validate it as an account name before passing it to a command; do not let untrusted text become a new option or a shell fragment.
The temporary files above are ordinary state changes, even though they are harmless output capture. Remove them once you are done:
$ rm -f /tmp/groups-check.out /tmp/groups-check.err /tmp/groups-missing.out /tmp/groups-missing.err
Warning
Do not use a shared predictable path in a privileged script. If a root-owned service needs this check, use a private temporary directory and review its file handling. The normal interactive examples here do not require elevation.
6. Keep permission changes separate
groups only reports information. It cannot add a user to a group, remove a membership or make a process reload credentials: those actions belong to account-management and session-management tools, and they can change who may read files, access devices or administer services.
If you have just changed membership, do not immediately assume a failed access test proves the change was wrong. Check the account with groups USERNAME, then start a fresh session or restart only the relevant process using your normal operational procedure. Before restarting a service, confirm its unit, maintenance window and rollback path: a restart can interrupt users even though groups itself is safe.
Use groups --help when you need the local synopsis. Use getent group GROUPNAME when you need the members recorded for one group rather than the groups associated with one user. These answer related but different questions.
Done means
- Confirmed the executable. And the installed GNU coreutils version.
- Used the right form. No username for the current process, a username for an account-database lookup.
- Know the refresh rule. A changed group database does not automatically refresh existing process credentials.
- Checked the exit status in scripts. Without parsing translated diagnostic text.
- Kept this read-only. Group-management and service-restart actions stayed out of it.