chgrp Command in Linux: Change File Group Ownership

Fix group ownership without changing file owners: practice chgrp on safe files, preview recursive targets, handle symlinks deliberately, and know when chmod or ACLs are the better follow-up.

PublishedAuthorJoshua JamesRead time11 minGuide typeLinux Commands

Shared Linux work stops being predictable when the group field is wrong. The chgrp command in Linux changes only the group owner of files and directories, which is the narrow fix when a project, service account, backup job, or shared directory needs group access without changing the user owner.

Use chgrp for group ownership, chown command examples when the user owner must change too, and chmod when the owner and group are already correct but the read, write, or execute bits are wrong.

Understand the chgrp Command in Linux

chgrp writes the group ownership field attached to a pathname. It does not add users to groups, create groups, or grant access by itself. The group must already exist, and the file permissions must still allow the group to read, write, or traverse the path after the ownership change.

Verify chgrp Is Available

Most full Linux distributions provide chgrp through a core utilities package, so you normally do not install it separately. Check the binary your shell will run:

command -v chgrp

A normal system returns a path such as:

/usr/bin/chgrp

Print implementation details when option behavior matters. Full coreutils implementations usually support --version, while smaller environments may expose a shorter help screen instead:

chgrp --version 2>/dev/null || chgrp --help

The command patterns use full coreutils behavior unless a section calls out an implementation difference. Older distro builds and minimal BusyBox-style images can omit long options such as --reference, --from, or --preserve-root, so check local help before using those flags in small containers, rescue shells, or older long-term-support systems.

chgrp Command Syntax

The common syntax sets a group explicitly or copies the group from a reference file:

chgrp [OPTIONS] GROUP FILE...
chgrp [OPTIONS] --reference=REFERENCE_FILE FILE...

The GNU chgrp manual documents extension options such as --from, --reference, and --preserve-root. Compatible implementations may support the same option names with different operand grammar, so check local help before scripting those forms. POSIX defines the portable baseline, including -h, -R, and the recursive symlink traversal flags -H, -L, and -P.

TaskCommand PatternWhat It Changes
Change one file groupchgrp developers report.txtSets the group owner of one file to developers.
Change several fileschgrp developers file1 file2Applies the same group to each listed pathname.
Copy from a reference filechgrp --reference=template.conf app.confUses the group owner of template.conf for app.conf.
Report only real changeschgrp -c developers filePrints a diagnostic only when the group actually changes.
Guard by current groupchgrp --from=:oldgroup newgroup fileChanges the file only when the current group matches oldgroup on newer GNU coreutils builds.
Change a directory treechgrp -R developers projectRecurses through the directory and its contents.
Change a symbolic link itselfchgrp -h developers linkChanges the link metadata instead of the target, when the system supports it.

A regular user can usually change a file to a group they belong to when they also own the file. Changing another user’s file, assigning a group outside your membership, or modifying protected system paths requires elevated privileges.

Create a Safe chgrp Practice Directory

Practice group changes in a disposable directory before touching service paths, web roots, backups, home directories, or shared project trees. This setup creates a unique directory in your home folder and selects one group from your current memberships so the examples do not require new system users or groups:

demo_dir=$(mktemp -d "$HOME/chgrp-demo.XXXXXX")
mkdir -p "$demo_dir/project/subdir" "$demo_dir/shared"
cd "$demo_dir"

printf 'draft\n' > report.txt
printf 'alpha\n' > alpha.txt
printf 'beta\n' > beta.txt
printf 'reference\n' > reference.txt
printf 'destination\n' > destination.txt
printf 'guarded\n' > guarded.txt
printf 'target\n' > symlink-target.txt
ln -s symlink-target.txt symlink-file
printf 'config\n' > project/app.conf
printf 'data\n' > project/subdir/data.txt
mkdir -p linked-subtree
printf 'linked\n' > linked-subtree/linked.txt
ln -s ../linked-subtree project/linked-subtree
ln -s project project-link

primary_group=$(id -gn)
target_group=$(id -Gn | tr ' ' '\n' | awk -v pg="$primary_group" '$0 != pg { print; exit }')

if [ -z "$target_group" ]; then
    target_group="$primary_group"
    printf 'Only one group found; examples will keep the same group name.\n'
fi

printf 'primary_group=%s\n' "$primary_group"
printf 'target_group=%s\n' "$target_group"

If your account belongs to only one group, the examples remain safe but some ownership checks will show the same group before and after the command. On a real multi-user system, replace $target_group with the project or service group you intend to use after confirming it exists.

Change File Group Ownership with chgrp

Start with single-file changes, then expand to multiple files and reference-based changes once the group field looks correct.

Change One File Group

Set the group owner of one file, then verify the owner and group fields with stat:

chgrp "$target_group" report.txt
stat -c 'owner=%U group=%G path=%n' report.txt

The group field in the stat output should match $target_group. Your user and group names will differ; the important check is that group= matches the target group. Example output when the owner is joshua and the target group is adm:

owner=joshua group=adm path=report.txt

If the command fails with Operation not permitted, check whether you own the file and whether your account belongs to the target group.

Change Multiple Files at Once

Pass several pathnames after the group name when the same group should own each file:

chgrp "$target_group" alpha.txt beta.txt
stat -c 'group=%G path=%n' alpha.txt beta.txt

Example output:

group=adm path=alpha.txt
group=adm path=beta.txt

Quote pathnames that contain spaces and use -- before names that start with a dash. That keeps the shell and chgrp from treating a file name as an option:

printf 'spaces\n' > "file with spaces.txt"
printf 'dash\n' > ./-dash-name.txt

chgrp "$target_group" "file with spaces.txt"
chgrp "$target_group" -- ./-dash-name.txt

Copy Group Ownership from a Reference File

Use --reference when a known-good file already has the group you want. This is safer than retyping group names in scripts that align several related files:

chgrp "$target_group" reference.txt
chgrp "$primary_group" destination.txt
chgrp --reference=reference.txt destination.txt
stat -c 'group=%G path=%n' reference.txt destination.txt

Both files should now show the same group:

group=adm path=reference.txt
group=adm path=destination.txt

The GNU manual notes that --reference dereferences a symbolic reference file, so a symlink reference uses the target’s group rather than the link’s own group.

Guard a Group Change by Current Group

--from narrows a change so a file is changed only when its current ownership matches the guard. Check local help first, because older GNU builds can omit this option and compatible implementations do not all use the same grammar:

chgrp --help 2>&1 | grep -- '--from'

If the help command prints no --from line, use a separate stat or find check before changing the group. Newer GNU coreutils builds show --from=CURRENT_OWNER:CURRENT_GROUP; use an empty owner before the colon when only the current group should match:

chgrp "$primary_group" guarded.txt
chgrp --from=:"$primary_group" "$target_group" guarded.txt
stat -c 'group=%G path=%n' guarded.txt

Do not drop the colon on GNU builds that document CURRENT_OWNER:CURRENT_GROUP. A command such as chgrp --from oldgroup newgroup file is parsed as an owner guard there, not as a group-only guard. If another implementation documents a different grammar, such as --from <GROUP>, follow that local help output instead of copying the GNU form blindly.

The verified GNU form leaves the file with the target group when the original group matched the guard. Example output:

group=adm path=guarded.txt

Use this pattern in scripts that repair ownership drift, especially when a file may have been replaced between the time you inspected it and the time you change it.

Use Numeric Group IDs Carefully

Group names are clearer for normal administration. Numeric GIDs are useful during migrations, container handoffs, or recovery work where names are missing or misleading. Full coreutils implementations support a leading + when you intend a numeric group ID instead of a possible numeric group name:

gid=$(getent group "$target_group" | cut -d: -f3)
chgrp +"$gid" report.txt
stat -c 'group=%G path=%n' report.txt

If getent group prints nothing, the group name is not available through the system group database. Fix the group name or create the group through your normal account-management process before using it in chgrp.

Directory and recursive group changes need more care than single-file changes. Preview the target set first, decide how symbolic links should behave, and verify the resulting group before moving to production paths.

Change a Directory Without Recursing

Changing a directory without -R updates only that directory entry. Existing files inside it keep their current group:

chgrp "$target_group" project
stat -c 'group=%G path=%n' project project/app.conf project/subdir/data.txt

Using the same example groups, only the directory entry changes while existing files inside it keep their previous group:

group=adm path=project
group=joshua path=project/app.conf
group=joshua path=project/subdir/data.txt

Use this form when only the directory itself needs a different group, such as when you are preparing permissions before new files are created.

Preview Recursive chgrp Targets

Preview the directory tree before a recursive ownership change. The -xdev option keeps find on the same filesystem, which helps avoid crossing into mounted backups, network shares, or bind mounts by accident. Use find command examples when you need deeper filters before a bulk ownership change.

find project -xdev -print | sort

Apply the recursive group change only after the preview contains the intended paths:

chgrp -R "$target_group" project
find project -xdev -exec stat -c 'group=%G path=%n' {} + | sort

Relevant output should show the target group on the directory and the in-tree entries that find reports. Symlink traversal needs its own decision, covered next:

group=adm path=project
group=adm path=project/app.conf
group=adm path=project/linked-subtree
group=adm path=project/subdir
group=adm path=project/subdir/data.txt

Use sudo only when the path actually requires administrative access. For system paths, pair recursion with a narrow target, a preview, and --preserve-root where the option is supported:

sudo chgrp -R --preserve-root developers /srv/project

Do not run recursive group changes from /, /etc, /usr, or a whole home directory unless you have a backup and a specific recovery plan. A correct group on the wrong tree is still a broken system.

Choose Symlink Behavior Explicitly

Non-recursive chgrp normally follows a symbolic link and changes the target. Use -h or --no-dereference when you need to change the link itself and the filesystem supports link ownership changes:

chgrp "$target_group" symlink-target.txt
chgrp -h "$primary_group" symlink-file
stat -c 'group=%G name=%N' symlink-file symlink-target.txt

The link and target can now show different groups:

group=joshua name='symlink-file' -> 'symlink-target.txt'
group=adm name='symlink-target.txt'

Recursive symlink traversal is easier to get wrong. GNU coreutils uses -P by default for recursive traversal, which does not follow symbolic links. POSIX does not require the same default for every implementation, so specify the traversal mode in scripts:

OptionRecursive Symlink BehaviorUse When
-PDoes not traverse symbolic links during -R.You want the safest default for project trees and scripts.
-HTraverses a command-line symlink that points to a directory.The starting path is a trusted directory symlink you intentionally named.
-LTraverses every symlink to a directory encountered during recursion.You have audited the tree and intentionally want linked directories included.

Use the practice directory to see the safer -P behavior. The symlink entry changes, but the file inside the linked directory stays on its original group:

chgrp "$primary_group" linked-subtree/linked.txt
chgrp -R -P "$target_group" project
stat -c 'group=%G path=%n' linked-subtree/linked.txt project/linked-subtree
group=joshua path=linked-subtree/linked.txt
group=adm path=project/linked-subtree

Use -L only when the linked directory should be included too:

chgrp "$primary_group" linked-subtree/linked.txt
chgrp -R -L "$target_group" project
stat -c 'group=%G path=%n' linked-subtree/linked.txt project/linked-subtree
group=adm path=linked-subtree/linked.txt
group=adm path=project/linked-subtree

Avoid -L on directories other users can modify. A symlink added during traversal can redirect a recursive group change outside the tree you meant to update.

Use chgrp for Shared Project Directories

A shared directory usually needs two pieces: the directory group should be the project group, and the setgid bit should be set so new files inherit that group. chgrp handles the group field; chmod handles the setgid mode bit.

Set a Shared Directory Group

Assign the directory to the target group, add the setgid bit, create a test file, and inspect the result:

chgrp "$target_group" shared
chmod 2775 shared
touch shared/new-note.txt
stat -c '%A %G %n' shared
stat -c 'group=%G path=%n' shared/new-note.txt

The directory mode should include an s in the group execute position, such as drwxrwsr-x. The new file should show the same group as the directory, even if that group is not your primary login group:

drwxrwsr-x adm shared
group=adm path=shared/new-note.txt

The setgid bit controls group inheritance, not the read or write bits on each new file. Those mode bits still come from the creating process and its umask.

Apply the Same Pattern to Existing Files

The setgid bit affects new entries. Existing files keep their old group until you change them, so align the current tree after setting the directory behavior:

find shared -xdev -print | sort
chgrp -R "$target_group" shared
find shared -xdev -exec stat -c 'group=%G path=%n' {} + | sort

For a real team directory, confirm every collaborator belongs to the project group before relying on group permissions. Group ownership does not help a user who is not a member of that group.

Apply chgrp to Common Group Ownership Tasks

These patterns show where chgrp fits in normal administration without turning it into a broad user-management or permissions runbook.

Hand Off Build Artifacts to a Project Group

Build tools often create output under the current user’s primary group. Change the group on the output directory before another project member needs to read or package it:

find project -xdev -print | sort
chgrp -R "$target_group" project
chmod -R g+rwX project
find project -xdev -exec stat -c '%A %G %n' {} + | sort

The chgrp command changes who the group owner is. The chmod command decides what that group can do; use the chmod permission examples when you need a fuller mode-bit workflow. Keep both steps visible so you can diagnose ownership and permission problems separately.

Align Configuration Files with a Template

When one file already has the correct group, copy only the group ownership from that file instead of hardcoding the group name in a script:

chgrp --reference=reference.txt destination.txt
stat -c '%G %n' reference.txt destination.txt

Use this for related files that should follow an existing local policy. It is less useful when the reference file may come from an archive or external system with stale ownership metadata.

Restore Group Ownership After an Archive Extract

Archives can preserve group names or IDs that do not match your current system. Inspect the extracted tree, then change only the extracted directory you own:

cp -a project extracted-project
find extracted-project -xdev -maxdepth 2 -exec stat -c '%U:%G %n' {} + | sort
chgrp -R "$target_group" extracted-project
find extracted-project -xdev -maxdepth 2 -exec stat -c '%G %n' {} + | sort

Do not run a broad recursive chgrp from the parent directory of several unrelated projects. Keep the target as narrow as the extracted or repaired tree.

Use ACLs When One Group Is Too Blunt

chgrp gives a path one owning group. If you need one extra user or a second group to access a file without changing the base owner and group model, use POSIX ACLs instead. LinuxCapable’s setfacl ACL permission examples cover named user, named group, mask, and default-directory ACL entries.

Troubleshoot Common chgrp Errors

Most chgrp failures come from one of four layers: the group does not exist, your account is not allowed to assign it, the file is not yours, or the permission bits still block access after the group changes.

Fix Operation Not Permitted

The error means the kernel rejected the group change. Start by checking the current file owner, current group, and your group memberships:

stat -c 'owner=%U group=%G path=%n' report.txt
id -Gn
getent group "$target_group"

If you own the file and belong to the target group, rerun the command without sudo first:

chgrp "$target_group" report.txt
stat -c '%U:%G %n' report.txt

If the file belongs to another user or the target group is outside your memberships, an administrator must either run the change or add your account to the group through the site’s normal account-management process. Open a new login session after group membership changes, because existing shells keep the old supplementary group list.

Fix Invalid Group

invalid group means the group operand did not resolve through the system group database. Verify the exact spelling before repeating the change:

getent group "$target_group"

A matching group prints a colon-separated record. No output means the group name is wrong, the group does not exist on that host, or the name service backing groups is unavailable. Fix that layer before using chgrp.

Fix Permission Denied After chgrp Succeeds

A successful group change does not grant access unless the group permission bits allow the operation. Check the mode and group together:

stat -c 'mode=%A owner=%U group=%G path=%n' report.txt

If the group is correct but the mode lacks group read, write, or directory execute permission, adjust the mode with chmod. For example, grant group read and write access to a normal file:

chmod g+rw report.txt
stat -c 'mode=%A owner=%U group=%G path=%n' report.txt

On directories, group execute permission controls traversal. A directory can show group read permission and still block access if the group execute bit is missing.

Fix Recursive chgrp That Missed Symlinked Directories

If recursive chgrp missed a symlinked directory, first identify whether the symlink was the starting path or a link discovered inside the tree. Inspect the link and the real target before repeating the recursive change:

stat -c 'group=%G name=%N' project-link project/linked-subtree linked-subtree/linked.txt

Use -H when the symlinked directory is the command-line operand, such as project-link in the practice directory:

chgrp -R -H "$target_group" project-link

Retest a real file below the target directory, not only the symlink entry:

stat -c 'group=%G path=%n' project/app.conf project/subdir/data.txt

Use -L only when linked directories discovered inside the tree should also be included:

chgrp -R -L "$target_group" project

Retest the file inside the linked directory after the -L fix:

stat -c 'group=%G path=%n' linked-subtree/linked.txt

Prefer -H when only the starting symlink is intentional. Use -L only after auditing the full tree, because it follows every directory symlink it encounters.

Fix setgid Directories That Do Not Affect Existing Files

The setgid bit on a directory changes group inheritance for new entries. It does not rewrite existing files. Confirm the directory mode and inspect the current file groups first:

stat -c '%A %G %n' shared
find shared -xdev -exec stat -c '%G %n' {} + | sort

Repair the existing tree after the diagnostic confirms old file groups remain:

chgrp -R "$target_group" shared

Retest by creating a new file after the directory group and setgid bit are correct.

touch shared/retest.txt
stat -c '%G %n' shared/retest.txt

Clean Up the chgrp Practice Directory

Remove the disposable practice directory when you are done. The command targets only the unique directory saved in $demo_dir earlier:

cd "$HOME"
[ -n "${demo_dir:-}" ] && rm -rf -- "$demo_dir"
unset demo_dir primary_group target_group gid

Conclusion

chgrp is the focused tool for group ownership changes: verify the current owner and group, preview recursive targets, choose symlink traversal deliberately, and pair group ownership with the right permission bits. For broader repairs, change user ownership separately; for finer access rules, use ACLs instead of forcing one group to fit every case.

Share this guide

Help another Linux user troubleshoot faster

Share this guide with someone troubleshooting Linux systems or saving it for later.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show more of our fresh Linux tutorials in Top Stories and From your sources when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<a href="https://example.com">link</a> link
<blockquote>quote</blockquote> quote block

Got a Question or Feedback?

We read and reply to every comment - let us know how we can help or improve this guide.

Verify before posting: