Overview
The chgrp (change group) command changes the group ownership of files and directories in Linux and Unix systems. Every file and directory has both an owner (user) and a group associated with it. The chgrp command specifically modifies the group ownership while leaving the user ownership unchanged.
Group ownership is a crucial part of Linux's permission system, enabling multiple users who are members of the same group to share access to files and directories. This is particularly important in multi-user environments, development teams, and server administration where controlled sharing of resources is necessary.
Key Characteristics
| Property | Description |
|---|---|
| Purpose | Change group ownership of files and directories |
| Permissions Required | File owner or root (with some restrictions) |
| Recursive Option | Yes (-R), applies to directories and their contents |
| Symlink Handling | Doesn't follow symlinks by default, options available |
groups command.
Understanding Linux Group Ownership
How Group Ownership Works
When you list files with ls -l, you see ownership displayed as:
^user ^group
The group field determines which group's permissions apply to the file. Group permissions are the middle set of three in the permission string:
^^^ user permissions (owner)
^^^ group permissions
^^^ other permissions
Common Groups on Linux Systems
| Group | Purpose | Typical Usage |
|---|---|---|
| wheel | System administrators (RHEL/CentOS) | Members can use sudo |
| sudo | System administrators (Debian/Ubuntu) | Members can use sudo |
| www-data | Web server processes | Web content ownership |
| apache/httpd | Apache web server | Web content ownership (RHEL) |
| docker | Docker users | Access to Docker daemon |
| adm | System monitoring | Read access to log files |
Detailed Examples
Example 1: Basic Group Change
-rw-r--r-- 1 craig users 2048 Nov 24 08:30 report.txt
$ chgrp developers report.txt
$ ls -l report.txt
-rw-r--r-- 1 craig developers 2048 Nov 24 08:30 report.txt
# Verify you're in the developers group
$ groups
craig wheel developers docker
# Can also use GID instead of group name
$ getent group developers
developers:x:1001:craig,john,jane
$ chgrp 1001 report.txt
$ ls -l report.txt
-rw-r--r-- 1 craig developers 2048 Nov 24 08:30 report.txt
getent group groupname to see a group's GID and members.
Example 2: Recursive Group Change for Directories
total 24
drwxr-xr-x 3 craig users 4096 Nov 24 08:30 .
drwxr-xr-x 5 craig users 4096 Nov 24 08:29 ..
-rw-r--r-- 1 craig users 1234 Nov 24 08:30 README.md
drwxr-xr-x 2 craig users 4096 Nov 24 08:30 src
-rw-r--r-- 1 craig users 567 Nov 24 08:30 config.yaml
$ chgrp -R developers project/
$ ls -la project/
total 24
drwxr-xr-x 3 craig developers 4096 Nov 24 08:30 .
drwxr-xr-x 5 craig users 4096 Nov 24 08:29 ..
-rw-r--r-- 1 craig developers 1234 Nov 24 08:30 README.md
drwxr-xr-x 2 craig developers 4096 Nov 24 08:30 src
-rw-r--r-- 1 craig developers 567 Nov 24 08:30 config.yaml
$ ls -la project/src/
-rw-r--r-- 1 craig developers 890 Nov 24 08:30 main.py
-rw-r--r-- 1 craig developers 456 Nov 24 08:30 utils.py
-R (recursive) option changes the group ownership of a directory and all its contents, including subdirectories and their files. This is essential when you want to share an entire project directory tree with a team. Notice that after the recursive change, all files and subdirectories under project/ now belong to the developers group. This is particularly useful when setting up shared project directories for development teams.
Example 3: Using --reference to Match Another File's Group
-rw-r--r-- 1 craig developers 1024 Nov 24 08:30 template.txt
-rw-r--r-- 1 craig users 2048 Nov 24 08:31 newfile.txt
# Make newfile.txt have the same group as template.txt
$ chgrp --reference=template.txt newfile.txt
$ ls -l template.txt newfile.txt
-rw-r--r-- 1 craig developers 1024 Nov 24 08:30 template.txt
-rw-r--r-- 1 craig developers 2048 Nov 24 08:31 newfile.txt
# Useful for multiple files
$ touch file1.txt file2.txt file3.txt
$ chgrp --reference=template.txt file*.txt
$ ls -l file*.txt
-rw-r--r-- 1 craig developers 0 Nov 24 08:32 file1.txt
-rw-r--r-- 1 craig developers 0 Nov 24 08:32 file2.txt
-rw-r--r-- 1 craig developers 0 Nov 24 08:32 file3.txt
--reference option is extremely useful when you want to match the group ownership of one or more files to that of a reference file. Instead of having to look up or remember what group a file belongs to, you simply point to a file with the desired ownership. This is particularly handy in scripts or when you want to ensure consistent group ownership across related files. You can use this with multiple target files by using wildcards or listing multiple files.
Example 4: Verbose Output for Confirmation
changed group of 'report1.txt' from users to developers
changed group of 'report2.txt' from users to developers
changed group of 'report3.txt' from users to developers
# Combined with recursive
$ chgrp -Rv www-data /var/www/html/
changed group of '/var/www/html/index.html' from root to www-data
changed group of '/var/www/html/css/style.css' from root to www-data
changed group of '/var/www/html/js/script.js' from root to www-data
changed group of '/var/www/html/images/logo.png' from root to www-data
...
# See only changes (not files already with correct group)
$ chgrp -cv developers *.txt
changed group of 'report1.txt' from users to developers
group of 'report2.txt' retained as developers
changed group of 'report3.txt' from users to developers
-v (verbose) option makes chgrp report each file it processes, showing what group change occurred. This is valuable for confirmation, logging, and troubleshooting. The -c (changes) option is similar but only reports when changes are actually made, not when the file already had the correct group. Verbose output is especially useful when performing operations on many files or when you want to create an audit trail of ownership changes. Combine with -R for detailed output during recursive operations.
-v or -c when making important system changes or when running as root. The confirmation output can be saved to a log file for audit purposes: chgrp -Rv www-data /var/www/html/ 2>&1 | tee chgrp.log
Example 5: Handling Symbolic Links
lrwxrwxrwx 1 craig users 10 Nov 24 08:30 mylink -> target.txt
$ ls -l target.txt
-rw-r--r-- 1 craig users 1024 Nov 24 08:30 target.txt
# Default behavior: changes the symlink itself (usually not what you want)
$ chgrp -h developers mylink
$ ls -l mylink
lrwxrwxrwx 1 craig developers 10 Nov 24 08:30 mylink -> target.txt
$ ls -l target.txt
-rw-r--r-- 1 craig users 1024 Nov 24 08:30 target.txt
# Change the target, not the symlink (typical usage)
$ chgrp developers mylink
$ ls -l target.txt
-rw-r--r-- 1 craig developers 1024 Nov 24 08:30 target.txt
# For recursive operations, control symlink traversal
$ chgrp -R developers project/ # Doesn't follow symlinks
$ chgrp -R -L developers project/ # Follows all symlinks
$ chgrp -R -H developers project/ # Follows symlinks on command line only
-h option, it changes the group of the target file, not the symlink itself. The -h (no-dereference) option changes the symlink itself. For recursive operations, you have three options: default behavior doesn't follow symlinks in subdirectories, -L follows all symbolic links, and -H follows only command-line symlinks. Understanding these options is crucial for managing permissions in directories with complex symlink structures.
Example 6: Changing Group on Web Server Content
$ sudo ls -l /var/www/html/
total 16
-rw-r--r-- 1 root root 4096 Nov 24 08:00 index.html
-rw-r--r-- 1 root root 2048 Nov 24 08:00 about.html
drwxr-xr-x 2 root root 4096 Nov 24 08:00 css
drwxr-xr-x 2 root root 4096 Nov 24 08:00 images
# Change group to www-data (web server user) recursively
$ sudo chgrp -R www-data /var/www/html/
# Verify the changes
$ sudo ls -l /var/www/html/
total 16
-rw-r--r-- 1 root www-data 4096 Nov 24 08:00 index.html
-rw-r--r-- 1 root www-data 2048 Nov 24 08:00 about.html
drwxr-xr-x 2 root www-data 4096 Nov 24 08:00 css
drwxr-xr-x 2 root www-data 4096 Nov 24 08:00 images
# Also set group permissions to allow web server to read
$ sudo chmod -R g+r /var/www/html/
$ sudo chmod -R g+X /var/www/html/ # Execute on directories only
# Add developers to www-data group for easy editing
$ sudo usermod -a -G www-data craig
$ groups craig
craig : craig wheel developers www-data
Example 7: Using chgrp in Scripts with Error Handling
#!/bin/bash
# Script to set up project directory with proper group ownership
PROJECT_DIR="/home/shared/newproject"
PROJECT_GROUP="developers"
# Check if group exists
if ! getent group "$PROJECT_GROUP" > /dev/null 2>&1; then
echo "Error: Group $PROJECT_GROUP does not exist"
exit 1
fi
# Check if directory exists
if [ ! -d "$PROJECT_DIR" ]; then
echo "Error: Directory $PROJECT_DIR does not exist"
exit 1
fi
# Change group ownership
if chgrp -R "$PROJECT_GROUP" "$PROJECT_DIR"; then
echo "Successfully changed group of $PROJECT_DIR to $PROJECT_GROUP"
# Also set setgid bit so new files inherit group
chmod -R g+s "$PROJECT_DIR"
echo "Set setgid bit on directories"
else
echo "Error: Failed to change group ownership"
exit 1
fi
# Report on changes
echo "Directory setup complete:"
ls -ld "$PROJECT_DIR"
$ sudo bash setup_project.sh
Successfully changed group of /home/shared/newproject to developers
Set setgid bit on directories
Directory setup complete:
drwxr-sr-x 5 root developers 4096 Nov 24 08:30 /home/shared/newproject
getent group, checks that the directory exists, and tests the return code of chgrp to ensure it succeeded. Additionally, it sets the setgid bit on directories (chmod g+s), which causes new files created in those directories to inherit the directory's group ownership rather than the creating user's default group. This is crucial for maintaining consistent group ownership in shared directories. Always include such validation and error handling in production scripts.
Example 8: Preserving Root Ownership with Selective Changes
$ sudo ls -l /etc/app/
total 32
-rw-r--r-- 1 root root 2048 Nov 24 08:00 app.conf
-rw-r--r-- 1 root root 1024 Nov 24 08:00 database.conf
-rw-r--r-- 1 root users 4096 Nov 24 08:00 users.conf
-rw-r--r-- 1 root users 8192 Nov 24 08:00 settings.conf
-rwxr-xr-x 1 root root 16384 Nov 24 08:00 init.sh
# Change only user-editable configs to a specific group
$ sudo chgrp appusers /etc/app/users.conf /etc/app/settings.conf
$ sudo ls -l /etc/app/
total 32
-rw-r--r-- 1 root root 2048 Nov 24 08:00 app.conf
-rw-r--r-- 1 root root 1024 Nov 24 08:00 database.conf
-rw-r--r-- 1 root appusers 4096 Nov 24 08:00 users.conf
-rw-r--r-- 1 root appusers 8192 Nov 24 08:00 settings.conf
-rwxr-xr-x 1 root root 16384 Nov 24 08:00 init.sh
# Or use find to select specific file types
$ sudo find /etc/app -name "*.conf" -not -name "app.conf" -not -name "database.conf" -exec chgrp appusers {} \;
# Verify critical files remain root-owned
$ sudo ls -l /etc/app/{app,database}.conf
-rw-r--r-- 1 root root 2048 Nov 24 08:00 /etc/app/app.conf
-rw-r--r-- 1 root root 1024 Nov 24 08:00 /etc/app/database.conf
-exec to selectively change groups based on filename patterns, file types, or other criteria. This approach is safer than recursive changes when working with system directories.
Example 9: Group Management for Shared Development Directories
$ sudo mkdir -p /projects/webapp/{src,docs,tests,deploy}
$ sudo chgrp -R developers /projects/webapp
$ sudo chmod -R 2775 /projects/webapp
# The 2 in 2775 sets the setgid bit
$ ls -ld /projects/webapp/*
drwxrwsr-x 2 root developers 4096 Nov 24 08:30 /projects/webapp/deploy
drwxrwsr-x 2 root developers 4096 Nov 24 08:30 /projects/webapp/docs
drwxrwsr-x 2 root developers 4096 Nov 24 08:30 /projects/webapp/src
drwxrwsr-x 2 root developers 4096 Nov 24 08:30 /projects/webapp/tests
# Notice the 's' in group permissions - that's setgid
# When a developer creates a file, it inherits the group
$ touch /projects/webapp/src/newfile.py
$ ls -l /projects/webapp/src/newfile.py
-rw-rw-r-- 1 craig developers 0 Nov 24 08:35 /projects/webapp/src/newfile.py
# Even though craig's primary group is 'craig', the file is in 'developers'
$ id -gn craig
craig
# Create subdirectory - it also inherits group and setgid
$ mkdir /projects/webapp/src/modules
$ ls -ld /projects/webapp/src/modules
drwxrwsr-x 2 craig developers 4096 Nov 24 08:36 /projects/webapp/src/modules
- 2770: Collaborative, no world access (most secure for team projects)
- 2775: Collaborative, world can read and traverse (good for docs)
- 2777: Fully open (rarely recommended, use with caution)
Example 10: Troubleshooting Group Ownership Issues
$ cat /shared/data.txt
cat: /shared/data.txt: Permission denied
# Check current ownership and permissions
$ ls -l /shared/data.txt
-rw-r----- 1 root datagroup 1024 Nov 24 08:00 /shared/data.txt
# Check if user is in the group
$ groups
craig wheel developers
# Problem: User is not in 'datagroup'
# Solution: Add user to group (requires root)
$ sudo usermod -a -G datagroup craig
# User needs to log out and back in, or use newgrp
$ newgrp datagroup
$ groups
datagroup wheel developers craig
# Now change the file if needed
$ sudo chgrp datagroup /shared/data.txt
# Verify access works
$ cat /shared/data.txt
This is the data file content.
# Diagnostic commands
$ id # Shows UID, GID, and all groups
uid=1000(craig) gid=1000(craig) groups=1000(craig),10(wheel),1001(developers),1002(datagroup)
$ getent group datagroup # Shows group members
datagroup:x:1002:craig,john,jane
# Find all files owned by a specific group
$ find /shared -group datagroup -ls
# Find files with wrong group ownership
$ find /shared -not -group datagroup -ls
ls -l, verifying group membership with groups, using id for detailed user information, checking group membership with getent group, and using find to locate files with specific or incorrect group ownership. The solution involves adding the user to the appropriate group with usermod, and the user must either log out/in or use newgrp for the change to take effect in their current session.
newgrp groupname starts a new shell with that group as the primary group, but logging out and back in is more thorough and is what's typically needed for services and GUI applications.
Common Options
| Option | Description | Example |
|---|---|---|
-R, --recursive |
Change files and directories recursively | chgrp -R devs /project |
-v, --verbose |
Output a diagnostic for every file processed | chgrp -v wheel file.txt |
-c, --changes |
Like verbose but report only when change is made | chgrp -c devs *.txt |
-f, --silent, --quiet |
Suppress most error messages | chgrp -f devs files/* |
--reference=RFILE |
Use RFILE's group rather than specifying a GROUP value | chgrp --reference=file1 file2 |
-h, --no-dereference |
Affect symbolic links instead of referenced files | chgrp -h devs mylink |
-H |
Follow command-line symlinks (with -R) | chgrp -R -H devs /dir |
-L |
Follow all symbolic links (with -R) | chgrp -R -L devs /dir |
-P |
Do not follow any symbolic links (default) | chgrp -R -P devs /dir |
Related Commands
Best Practices and Tips
Always set the setgid bit (chmod g+s) on shared directories after changing their group. This ensures new files inherit the directory's group:
sudo chmod -R 2775 /shared/project
Before changing files to a group, verify users are actually members:
groups username
When you want to match existing ownership, use --reference instead of manually specifying groups:
- Running recursive chgrp on system directories without careful consideration
- Forgetting that group membership changes require logout/login to take effect
- Not setting setgid bit on collaborative directories
- Changing groups without verifying group exists and users are members
- Using numeric GIDs in scripts (not portable across systems)
Exit Status
| Code | Meaning |
|---|---|
| 0 | Success - group ownership changed |
| 1 | Failure - permission denied, group doesn't exist, or file not found |