Python中递归设置文件权限的方法是什么?

54

如何在Python中递归地将文件夹中的所有文件的所有者和组设置为特定的值?我可以使用'chown -R'命令来调用shell,但是我感觉缺少了一些明显的方法。

我正在尝试以下代码:


import os  
path = "/tmp/foo"  
for root, dirs, files in os.walk(path):  
  for momo in dirs:  
    os.chown(momo, 502, 20)

在设置目录时,这似乎是有效的,但应用于文件时失败了。我怀疑文件没有获取到完整路径,因此chown失败,因为它找不到这些文件。错误信息为:

'OSError: [Errno 2] No such file or directory: 'foo.html'

我在这里忽略了什么?

10个回答

47

dirsfiles列表始终相对于root - 即它们是文件/文件夹的basename(),即它们没有/(或在Windows上为\)。如果您希望您的代码能够无限递归地工作,则需要将dirs/files连接到root以获取其完整路径:


import os  
path = "/tmp/foo"
    
# Change permissions for the top-level folder
os.chmod(path, 502, 20)

for root, dirs, files in os.walk(path):
  # set perms on sub-directories  
  for momo in dirs:
    os.chown(os.path.join(root, momo), 502, 20)

  # set perms on files
  for momo in files:
    os.chown(os.path.join(root, momo), 502, 20)

令人惊讶的是,shutil模块没有这个功能的函数。


7
这段代码有一个漏洞,我刚在同事的生产代码中看到了:-) 没有改变指定的顶层目录的所有者。我提出了一个修复的建议,希望能得到批准。 - Avindra Goolcharan
5
我的编辑被拒绝了 - 祝愿任何使用此代码并遇到“/tmp/foo”未更改权限的错误的人好运。很好,SO的Python社区管理者。 - Avindra Goolcharan
4
@AvindraGoolcharan 很好的发现 - 希望这正是你所想要的! - fatal_error
6
不必遍历dirs,因为os.walk会遍历所有目录。参见我的答案 - Christian Alis
3
@AvindraGoolcharan 编辑 1, 2,撤销 3 就出局了。显然,9位审阅者认为添加 os.chown(path, 502, 20) "偏离了意图","应该是一条注释",或者"值得为之新建一个完整的答案"。真的吗...?只有一行代码?所有这些人都不知道 chmod -R some_dir 可以改变 some_dir 和它所有的子目录吗?我知道你们都很害怕批准代码编辑,但是,这太荒谬了。 - jrh
显示剩余2条评论

29

正如上面正确指出的那样,被接受的答案遗漏了顶级文件和目录。其他答案使用os.walk然后循环遍历dirnamesfilenames。但是,os.walk无论如何都会通过dirnames,所以您可以跳过循环遍历dirnames,只需将当前目录(dirpath)的chown操作即可:

def recursive_chown(path, owner):
    for dirpath, dirnames, filenames in os.walk(path):
        shutil.chown(dirpath, owner)
        for filename in filenames:
            shutil.chown(os.path.join(dirpath, filename), owner)

1
为什么要使用shutil.chown而不是os.chown? - gerardw
4
@gerardw 我相信 os.chown() 只接受数字 uid 和 gid,而 shutil.chown() 则可以接受名称或数字 ID。 - wedgef5

16

我可以直接向shell传递'chown -R'命令

这是最简单的方法,可能在问题中有些被忽略了,所以为了清晰起见,如果您不关心Windows,您可以在一行代码中完成:

os.system('chown -R 502 /tmp/foo')

2
这个功能没有得到足够的认可。让shutil获得递归功能会很好,但在那之前,为什么要费心呢? - TTimo

7
import os  
path = "/tmp/foo"  
for root, dirs, files in os.walk(path):  
  for momo in dirs:  
    os.chown(momo, 502, 20)
  for file in files:
     fname = os.path.join(root, file)
     os.chown(fname, aaa, bb)

请随意替换aaabb


1
正如接受的评论所述,/tmp/foo 的所有者不会被正确设置。请参见我上面的评论。 - Avindra Goolcharan

4
尝试使用os.path.join(root,momo),这将给你完整的路径。

2

这里是我编写的一个函数,使用glob递归列出文件并更改它们的权限。

import os
import glob
def recursive_file_permissions(path,mode,uid=-1,gid=-1):
        '''
        Recursively updates file permissions on a given path.
        UID and GID default to -1, and mode is required
        '''
    for item in glob.glob(path+'/*'):
        if os.path.isdir(item):
            recursive_file_permissions(os.path.join(path,item),mode,uid,gid)
        else:
            try:
                os.chown(os.path.join(path,item),uid,gid)
                os.chmod(os.path.join(path,item),mode)
            except:
                print('File permissions on {0} not updated due to error.'.format(os.path.join(path,item)))

虽然不完美,但它帮我到达了目的地。


2

被接受的答案忽略了顶级文件。这是chown -R的实际等效命令。

import os

path = "/tmp/foo"

os.chown(path, 502, 20)
for dirpath, dirnames, filenames in os.walk(path):
    for dname in dirnames:
        os.chown(os.path.join(dirpath, dname), 502, 20)
    for fname in filenames:
        os.chown(os.path.join(dirpath, fname), 502, 20)

2
不需要遍历dirnames,因为os.walk会遍历所有目录。请参见我的答案 - Christian Alis

1
不要忘记 for f in files 循环。同样,记得使用 os.path.join(root, f) 获取完整路径。

0
"""
Requires python 3
Accepts name or id
Usage:
  chown.py -p /temp/folder -u user  -g group -r true
  or
  chown.py -p /temp/folder -u uid -g gid -r 1
  user, group, and recursive are optional
  But must supply at least one of user or group
Example: sudo chown.py -p /temp/filename -u some_user 
"""
import argparse, os, sys
from shutil import chown
user = group = recursive = ''
parser=argparse.ArgumentParser()
parser.add_argument('-p', '--path')  # help='file/path'
parser.add_argument('-u', '--user')   # , help='user'
parser.add_argument( '-g','--group')   # , help='group'
parser.add_argument('-r', '--recursive', help=1)  # , help='recursive'

args=parser.parse_args()
path = args.path
if not path:
    raise Exception('missing path')
if args.user:
    user = args.user
if args.group:
    user = args.group
if args.recursive:
    recursive = True

if not user and not group:
    raise Exception('must supply user, group, or both')

def change_owner(path, user='', group='')
    if user and not group:
        chown(path, user=user)
    elif not user and group:
        chown(path, group=group)
    else:
        chown(path, user, group)

change_owner(path, user, group)
if recursive:
    for dirpath, dirnames, filenames in os.walk(path):
        for dname in dirnames:
            change_owner(os.path.join(dirpath, dname), user, group)
        for fname in filenames:
            change_owner(os.path.join(dirpath, fname), user, group)

-2

使用os.lchown代替os.chown来同时更改链接本身和文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接