ZetCode

Python os.umask 函数

上次修改时间:2025 年 4 月 11 日

本综合指南探讨了 Python 的 os.umask 函数,该函数设置文件模式创建掩码。我们将涵盖权限位、掩码计算和实际的文件创建示例。

基本定义

os.umask 函数设置当前数字 umask 值并返回先前的值。umask 决定了默认的文件权限。

umask 从默认权限(文件通常为 666,目录通常为 777)中减去,以确定实际权限。它使用八进制表示法。

获取当前 Umask 值

要检索当前的 umask 而不更改它,请使用当前值调用 os.umask。 这会在读取现有掩码的同时保留它。

get_umask.py
import os

# Get current umask without changing it
current_umask = os.umask(0)
os.umask(current_umask)  # Restore original umask

print(f"Current umask: {oct(current_umask)}")
print(f"Interpreted as: {current_umask:03o}")

# Typical default umask values
print("\nCommon umask values:")
print("022 - Owner has full, group/others read/execute")
print("002 - Owner/group have full, others read/execute")
print("077 - Only owner has full access")

此示例展示了如何安全地读取当前 umask 值。 八进制格式表示默认情况下将禁用哪些权限位。

umask 是进程特定的,并且会影响该进程中所有后续的文件/目录创建。

设置新的 Umask 值

要更改 umask,请将新值传递给 os.umask。该函数返回先前的 umask 值。为清楚起见,请使用八进制表示法。

set_umask.py
import os

# Save current umask
original_umask = os.umask(0)
os.umask(original_umask)  # Restore immediately

print(f"Original umask: {oct(original_umask)}")

# Set restrictive umask (owner only)
new_umask = 0o077
old_umask = os.umask(new_umask)
print(f"Changed umask from {oct(old_umask)} to {oct(new_umask)}")

# Create file with new umask
with open("restricted_file.txt", "w") as f:
    f.write("Secret content")

# Restore original umask
os.umask(original_umask)
print(f"Restored original umask: {oct(original_umask)}")

这演示了如何临时更改 umask 以创建具有限制性权限的文件,然后恢复原始 umask。

umask 会影响所有后续文件操作,直到再次更改或进程结束。

理解 Umask 计算

umask 的工作原理是从默认模式中减去权限位。 对于文件,默认为 666 (rw-rw-rw-),对于目录,默认为 777 (rwxrwxrwx)。

umask_calculation.py
import os
import stat

def show_permissions(path):
    mode = os.stat(path).st_mode
    print(f"{path}: {oct(mode & 0o777)}")

# Default umask (usually 022)
default_umask = 0o022
os.umask(default_umask)

# Create file and directory
with open("normal_file.txt", "w") as f:
    f.write("Test content")
os.mkdir("normal_dir")

show_permissions("normal_file.txt")  # Should be 644 (666 - 022)
show_permissions("normal_dir")       # Should be 755 (777 - 022)

# Change umask to 077 (restrictive)
os.umask(0o077)
with open("private_file.txt", "w") as f:
    f.write("Private content")
os.mkdir("private_dir")

show_permissions("private_file.txt")  # Should be 600 (666 - 077)
show_permissions("private_dir")       # Should be 700 (777 - 077)

此示例展示了不同的 umask 值如何影响文件和目录权限。 实际权限是通过与 umask 补码按位与计算得出的。

umask 不授予权限 - 它仅限制默认权限。

临时 Umask 上下文

为了安全地更改 umask,请使用上下文管理器,该管理器会自动恢复原始 umask。 这可以防止意外的权限泄露。

umask_context.py
import os
from contextlib import contextmanager

@contextmanager
def temp_umask(new_umask):
    old_umask = os.umask(new_umask)
    try:
        yield
    finally:
        os.umask(old_umask)

# Original umask
print(f"Start umask: {oct(os.umask(0))}")
os.umask(0o022)  # Reset to common default

# Use restrictive umask temporarily
with temp_umask(0o077):
    print(f"Inside context umask: {oct(os.umask(0))}")
    os.umask(0o077)  # Restore within context
    with open("temp_secure.txt", "w") as f:
        f.write("Temporary secure file")

# Verify umask restored
print(f"After context umask: {oct(os.umask(0))}")
os.umask(0o022)  # Clean up

上下文管理器确保始终恢复 umask,即使发生异常也是如此。 这比手动 umask 管理更安全。

这种模式在库中尤其有用,因为您无法预测代码的使用方式。

带有 os.makedirs 的 Umask

使用 os.makedirs 递归创建目录时,umask 会影响所有创建的目录。 此示例演示了该行为。

makedirs_umask.py
import os
import stat

def show_tree_permissions(root):
    for dirpath, dirnames, filenames in os.walk(root):
        print(f"\nDirectory: {dirpath}")
        mode = os.stat(dirpath).st_mode
        print(f"Permissions: {oct(mode & 0o777)}")
        for f in filenames:
            path = os.path.join(dirpath, f)
            mode = os.stat(path).st_mode
            print(f"File {f}: {oct(mode & 0o777)}")

# Set restrictive umask
os.umask(0o077)

# Create directory tree
try:
    os.makedirs("secure_tree/sub1/sub2")
    with open("secure_tree/file1.txt", "w") as f:
        f.write("Top level")
    with open("secure_tree/sub1/file2.txt", "w") as f:
        f.write("Sub level")
    
    show_tree_permissions("secure_tree")
finally:
    # Clean up
    os.umask(0o022)
    # Remove test directories
    import shutil
    shutil.rmtree("secure_tree", ignore_errors=True)

在限制性 umask 下创建的所有目录和文件都继承了受限权限。 umask 影响整个操作。

这展示了 umask 如何提供一种在整个应用程序中强制执行权限策略的方法。

平台特定的 Umask 行为

Umask 行为在 Unix 和 Windows 系统之间略有不同。 此示例演示了这些差异以及如何处理它们。

platform_umask.py
import os
import sys
import stat

def create_test_file():
    test_file = "umask_test.txt"
    with open(test_file, "w") as f:
        f.write("Platform test")
    return test_file

# Get current umask
current_umask = os.umask(0)
os.umask(current_umask)

print(f"Platform: {sys.platform}")
print(f"Current umask: {oct(current_umask)}")

# Windows specific behavior
if sys.platform == "win32":
    print("\nWindows notes:")
    print("- Umask affects only the execute bit")
    print("- Read/write permissions not fully enforced")
    print("- Default umask is usually 0o022")
    
    # Windows ignores some umask bits
    os.umask(0o077)  # Try to set restrictive
    test_file = create_test_file()
    mode = os.stat(test_file).st_mode
    print(f"Created file permissions: {oct(mode & 0o777)}")
    os.unlink(test_file)
else:
    # Unix behavior
    print("\nUnix notes:")
    print("- Umask fully affects all permission bits")
    print("- Strict enforcement of permissions")
    
    os.umask(0o077)  # Restrictive umask
    test_file = create_test_file()
    mode = os.stat(test_file).st_mode
    print(f"Created file permissions: {oct(mode & 0o777)}")
    os.unlink(test_file)

# Restore original umask
os.umask(current_umask)

在 Unix 系统上,umask 完全控制权限。 Windows 的支持有限,主要影响执行位。

编写跨平台代码时,不要仅依赖 umask 来保证安全。 添加显式的权限检查。

安全注意事项

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程