Python模块os.chmod(file, 664)不能将权限更改为rw-rw-r--,而是-w--wx----。

143

最近我正在使用Python的os模块,当我尝试更改文件权限时,没有得到预期的结果。例如,我想将权限更改为rw-rw-r--,

os.chmod("/tmp/test_file", 664)

所有权权限实际上是-w--wx---(230)
--w--wx--- 1 ag ag 0 Mar 25 05:45 test_file

然而,如果我在代码中将664更改为0664,则结果正是我所需要的,例如:

os.chmod("/tmp/test_file", 0664)

结果是:

-rw-rw-r-- 1 ag ag 0 Mar 25 05:55 test_file

有人能帮忙解释为什么在获取正确结果时前导零很重要吗?


50
八进制。八进制。八进制。 - Cole Tobin
10
我在http://bugs.python.org/issue25377为Python文档问题提出了一个请求,因为这应该在文档中明确表述。 - Kalle Richter
7个回答

173

我在另一个论坛上找到了这个。

如果你想知道为什么前导零很重要,那是因为权限被设置为八进制整数,而Python自动将任何带前导零的整数视为八进制。所以os.chmod("file", 484)(十进制)会得到相同的结果。

你正在传递664,在八进制中是1230

在你的情况下,你需要

os.chmod("/tmp/test_file", 436)

[更新] 对于Python 3,你需要以0o(零欧)为前缀。例如:0o666

1
谢谢,但我仍然困惑,十进制中的484意味着八进制中的744,在你提到的论坛帖子中这是有道理的。然而,如果我输入十进制中的644,则会变成八进制中的1204。那么1204与八进制中的230有什么关系呢? - AplusG
3
@AplusG:1 并没有 被丢弃!那是粘滞/设置用户ID/设置组ID位,而 1 表示 粘滞。使用 ls -l 命令,你可以注意到权限现在在结尾处包括一个 T... - MestreLion
2
更容易添加0并将其转换为八进制 :) - radtek
15
注意,对于 Python 3,你需要以 0o(零 oh)作为前缀。 - Mawg says reinstate Monica
4
我在 Python 2.7.10 中使用 0o。 - Wyrmwood
显示剩余5条评论

153

所以对于希望与以下语义类似的人:

$ chmod 755 somefile

使用:

$ python -c "import os; os.chmod('somefile', 0o755)"

如果你的 Python 版本低于 2.6:

$ python -c "import os; os.chmod('somefile', 0755)"

13
Python3格式在Python 2.7.9中也能够使用。我没有检查之前的版本。 - Fred Mitchell
3
Python 3的语法向后兼容到Python 2.6。详见:https://docs.python.org/3/whatsnew/2.6.html#pep-3127-integer-literal-support-and-syntax - Pete
为我工作,谢谢! - LandiLeite
1
最好设为 00755,这样后来的开发者如果想要在这个旧脚本中使用 2755 的 sgid 时,就能够清楚地知道 suid/sgid/sticky 位应该放置在哪里,避免权限出现混乱。;) - dannysauer

23

使用权限符号(stat.S_I*)代替八进制数字

如果您使用更具语义的权限符号而不是原始魔术数字,例如664,则可以避免出现问题。

#!/usr/bin/env python3

import os
import stat

os.chmod(
    'myfile',
    stat.S_IRUSR |
    stat.S_IWUSR |
    stat.S_IRGRP |
    stat.S_IWGRP |
    stat.S_IROTH
)

这在https://docs.python.org/3/library/os.html#os.chmod有记录,名称与POSIX C API相同,该API也在man 2 statman 2 chmod中。

S_IRUSR  (00400)  read by owner
S_IWUSR  (00200)  write by owner
S_IXUSR  (00100)  execute/search by owner
S_IRGRP  (00040)  read by group
S_IWGRP  (00020)  write by group
S_IXGRP  (00010)  execute/search by group
S_IROTH  (00004)  read by others
S_IWOTH  (00002)  write by others
S_IXOTH  (00001)  execute/search by others

另一个优点是更强的可移植性,正如文档中所述:

注意:尽管 Windows 支持 chmod(),但你只能使用它来设置文件的只读标志(通过 stat.S_IWRITEstat.S_IREAD 常量或相应的整数值)。所有其他位都将被忽略。

chmod +x 的演示在此处: 如何从 Python 中进行简单的“chmod +x”?

在 Ubuntu 16.04,Python 3.5.2 中测试通过。


1
这是处理问题的正确方式。 - carthurs
5
我知道这是 Python 的写法,但它几乎无法更丑陋了 :) - Tomasz Swider

14

如果您已将所需权限保存为字符串,则执行以下操作:

s = '660'
os.chmod(file_path, int(s, base=8))

优秀的现实用例 - Craig Jackson

13

前导的0表示这是一个八进制常量,而不是十进制的。你需要一个八进制来更改文件模式。

权限是一个位掩码,例如,rwxrwx---在二进制中是111111000,很容易将位分组成3个以转换为八进制,然后计算其十进制表示。

0644(八进制)对应二进制为0.110.100.100(我加点以方便阅读),或者,您可以计算出其十进制表示为420


5

@mc.dev的答案是这里最好的答案,我最终利用它来制作下面的函数包装器以便重用。感谢分享。

def chmod_digit(file_path, perms):
    """
    Helper function to chmod like you would in unix without having to preface 0o or converting to octal yourself.
    Credits: https://dev59.com/O2Uo5IYBdhLWcg3w_DpK#60052847
    """
    os.chmod(file_path, int(str(perms), base=8))

0

使用 stat.* 位掩码似乎是最具可移植性和明确性的方法。但另一方面,我经常忘记如何最好地处理它。因此,这里有一个例子,遮蔽“组”和“其他”权限,保留“所有者”权限不变。使用位掩码和减法是一种有用的模式。

import os
import stat
def chmodme(pn):
    """Removes 'group' and 'other' perms. Doesn't touch 'owner' perms."""
    mode = os.stat(pn).st_mode
    mode -= (mode & (stat.S_IRWXG | stat.S_IRWXO))
    os.chmod(pn, mode)

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