ZetCode

Python os.chown Function

Last modified April 11, 2025

This comprehensive guide explores Python's os.chown function, which changes file ownership by UID and GID. We'll cover Unix permissions, user/group management, and practical file system examples.

Basic Definitions

The os.chown function changes the owner and group of a file. It's available on Unix-like systems and requires appropriate permissions.

Key parameters: path (file/directory), uid (user ID), gid (group ID). Use -1 to leave either uid or gid unchanged. Requires root privileges.

Changing File Ownership

The most basic use of os.chown changes both owner and group of a file. This requires root privileges or ownership of the file.

change_ownership.py
import os
import pwd
import grp

file_path = "example.txt"

# Get current ownership
stat_info = os.stat(file_path)
print(f"Current owner: {pwd.getpwuid(stat_info.st_uid).pw_name}")
print(f"Current group: {grp.getgrgid(stat_info.st_gid).gr_name}")

# Change to new owner (www-data) and group (www-data)
new_uid = pwd.getpwnam("www-data").pw_uid
new_gid = grp.getgrnam("www-data").gr_gid

os.chown(file_path, new_uid, new_gid)
print("Ownership changed successfully")

# Verify changes
stat_info = os.stat(file_path)
print(f"New owner: {pwd.getpwuid(stat_info.st_uid).pw_name}")
print(f"New group: {grp.getgrgid(stat_info.st_gid).gr_name}")

This example first shows current ownership, then changes it to www-data user and group. It verifies the changes afterward using os.stat.

Note that this requires appropriate permissions - typically root access or ownership of the file being modified.

Changing Only User Ownership

To change only the user owner while keeping the group unchanged, pass -1 as the gid parameter. This demonstrates selective modification.

change_user_only.py
import os
import pwd

file_path = "data.log"

# Get current ownership
stat_info = os.stat(file_path)
current_owner = pwd.getpwuid(stat_info.st_uid).pw_name
print(f"Current owner: {current_owner}")

# Change only the user to 'backup'
new_uid = pwd.getpwnam("backup").pw_uid
os.chown(file_path, new_uid, -1)

# Verify changes
stat_info = os.stat(file_path)
new_owner = pwd.getpwuid(stat_info.st_uid).pw_name
print(f"New owner: {new_owner}")
print(f"Group remains: {stat_info.st_gid}")

This script changes only the user owner while preserving the existing group ownership. The -1 parameter indicates no change to group ownership.

This is useful when you need to change ownership but want to maintain existing group permissions on the file.

Changing Only Group Ownership

Similarly, you can change only the group ownership by passing -1 as uid. This leaves the user owner unchanged while modifying the group.

change_group_only.py
import os
import grp

file_path = "config.ini"

# Get current group
stat_info = os.stat(file_path)
current_group = grp.getgrgid(stat_info.st_gid).gr_name
print(f"Current group: {current_group}")

# Change only the group to 'adm'
new_gid = grp.getgrnam("adm").gr_gid
os.chown(file_path, -1, new_gid)

# Verify changes
stat_info = os.stat(file_path)
new_group = grp.getgrgid(stat_info.st_gid).gr_name
print(f"New group: {new_group}")
print(f"User remains: {stat_info.st_uid}")

This example demonstrates changing only the group ownership while keeping the user owner the same. The -1 for uid indicates no user change.

This is commonly used when adjusting group permissions without affecting individual file ownership.

Changing Directory Ownership Recursively

To change ownership of a directory and all its contents, we need to walk through the directory tree. This example shows a recursive implementation.

recursive_chown.py
import os
import pwd
import grp

def recursive_chown(path, uid, gid):
    for root, dirs, files in os.walk(path):
        for d in dirs:
            os.chown(os.path.join(root, d), uid, gid)
        for f in files:
            os.chown(os.path.join(root, f), uid, gid)
    os.chown(path, uid, gid)

directory = "/var/www/myapp"
new_uid = pwd.getpwnam("www-data").pw_uid
new_gid = grp.getgrnam("www-data").gr_gid

print(f"Changing ownership of {directory} recursively...")
recursive_chown(directory, new_uid, new_gid)
print("Ownership changed successfully")

This function uses os.walk to traverse all directories and files within the specified path. It applies chown to each item found.

Recursive ownership changes are common when deploying web applications or setting up shared directories with specific permissions.

Handling Permission Errors

Attempting to change ownership without sufficient privileges raises PermissionError. This example shows proper error handling.

handle_errors.py
import os
import sys
import pwd

file_path = "/etc/nginx/nginx.conf"

try:
    # Try to change to current user
    current_uid = os.getuid()
    os.chown(file_path, current_uid, -1)
    print("Ownership changed successfully")
except PermissionError:
    print("Error: Permission denied. Need root privileges.", file=sys.stderr)
    sys.exit(1)
except FileNotFoundError:
    print(f"Error: File {file_path} not found", file=sys.stderr)
    sys.exit(1)
except Exception as e:
    print(f"Unexpected error: {str(e)}", file=sys.stderr)
    sys.exit(1)

This script attempts to change ownership of a system file, which typically requires root privileges. It catches and handles various error cases.

Proper error handling is crucial when working with file ownership changes, as failed attempts can leave systems in inconsistent states.

Using os.chown with Symbolic Links

By default, os.chown affects symbolic links themselves. To change the target's ownership, use os.lchown or resolve the link first.

symlink_chown.py
import os
import pwd

# Create a symbolic link for demonstration
target = "original.txt"
link_name = "symlink.txt"
with open(target, "w") as f:
    f.write("Test file")
os.symlink(target, link_name)

# Change ownership of the link itself
os.chown(link_name, os.getuid(), os.getgid())

# Change ownership of the target file
resolved_path = os.path.realpath(link_name)
os.chown(resolved_path, pwd.getpwnam("nobody").pw_uid, -1)

# Verify changes
link_stat = os.lstat(link_name)
target_stat = os.stat(resolved_path)
print(f"Link owner: {link_stat.st_uid}")
print(f"Target owner: {target_stat.st_uid}")

# Clean up
os.unlink(link_name)
os.unlink(target)

This example demonstrates the difference between changing ownership of a symbolic link versus its target file. The lstat function shows link metadata without following it.

Understanding this distinction is important when managing systems with many symbolic links, such as package-managed configurations.

Security Considerations

Best Practices

Source References

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.