如何在Python中执行简单的“chmod +x”操作?

173

我想从Python脚本中创建一个可执行的文件。

import os
import stat
os.chmod('somefile', stat.S_IEXEC)

看起来os.chmod无法像unix中的chmod那样“添加”权限。如果将最后一行注释掉,文件的文件模式为-rw-r--r--,如果不注释,则文件模式为---x------。如何在保持其余模式不变的情况下仅添加u+x标志?

9个回答

270

使用 os.stat() 获取当前权限,使用 | 来对位进行 OR 操作,然后使用 os.chmod() 来设置更新后的权限。

示例:

import os
import stat

st = os.stat('somefile')
os.chmod('somefile', st.st_mode | stat.S_IEXEC)

4
这仅使其对用户可执行。发帖者询问的是“chmod +x”,它使其在全局范围内(用户、组、全世界)都可以执行。 - eric.frederich
42
使用以下内容,使它可供每个人执行... stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH。注意:该值与八进制数0111相同,因此您可以只使用st.st_mode | 0111。 - eric.frederich
2
我的答案将 R 位复制到 X,就像编译器一样可以预期的那样。 - Jonathon Reinhart
1
我会使用STAT_OWNER_EXECUTABLE = stat.S_IEXEC,并使用可读性更好的本地常量,而不是无意义的常量。 - ThorSummoner
2
以下是一个非 Pythonic 的答案,可能更易读:subprocess.check_call(['chmod', '+x', 'somefile']),并且可以更轻松地执行像 a+rx 这样的操作。 - Trevor Boyd Smith
显示剩余2条评论

32

对于生成可执行文件(例如脚本)的工具,下面的代码可能会有所帮助:

def make_executable(path):
    mode = os.stat(path).st_mode
    mode |= (mode & 0o444) >> 2    # copy R bits to X
    os.chmod(path, mode)

这会使其(或多或少)遵守创建文件时生效的umask,只有那些可以读取的人才能设置可执行权限。

用法:

path = 'foo.sh'
with open(path, 'w') as f:           # umask in effect when file is created
    f.write('#!/bin/sh\n')
    f.write('echo "hello world"\n')

make_executable(path)

2
Python 3 中的八进制字面量已经发生了变化。现在你需要使用 0o444 代替 0444。或者,如果你想要同时支持两种写法,可以直接写 292 - Kevin
1
@Kevin 看起来 Python 2.6 已经支持了新语法,因此使用它似乎是合理的选择。(作为兼容性参考,CentOS 6 使用的是 Python 2.6)。 - Jonathon Reinhart
2
我不知道Python 3已经删除了传统的八进制字面量。所以谢谢你提醒我。 - Jonathon Reinhart
os.chmod("path", os.stat("path").st_mode | 0o111) 这行代码与下面的代码有相同的效果,但是它只有一行。 - Aarav Prasad
1
@AaravPrasad 不,它们不一样。你打开了所有X位,而我的答案只打开已经打开R的X位。此外,“一行代码”对于任何真实项目来说都毫无价值。 - Jonathon Reinhart
显示剩余2条评论

26

如果你使用的是Python 3.4+,你可以使用标准库方便的pathlib

它的Path类内置了chmodstat方法。

from pathlib import Path
import stat


f = Path("/path/to/file.txt")
f.chmod(f.stat().st_mode | stat.S_IEXEC)

没有导入 stat,它就无法正常工作。 - jaromrax

18

如果您知道所需的权限,则以下示例可能是保持简单的方法。

Python 2:

os.chmod("/somedir/somefile", 0775)

Python 3:

os.chmod("/somedir/somefile", 0o775)

兼容以下两种格式(八进制转换):

os.chmod("/somedir/somefile", 509)

参考 权限示例


4
这应该是os.chmod("/somedir/somefile", 0o775)。 - ang mo

9

像使用 chmod +x 一样尊重 umask

man chmod 命令中提到,如果未指定 augo 参数:

chmod +x mypath

接下来使用a,但同时使用umask

字母组合(u)ser、(g)roup、(o)ther和(a)ll控制着将更改哪些用户对文件的访问权限:拥有文件的用户(u)、文件所属组中的其他用户(g)、未在文件所属组中的其他用户(o)或所有用户(a)。如果没有指定上述任何选项,则效果好像指定了(a),但是受到umask设置的位不会受到影响。

这样做的目的是为了避免意外赋予过多权限。umask决定了新文件的默认权限,例如umask为0077,则touch newfile.txt产生的权限为当前用户的rw,因为77会排除组和其他权限(touch默认情况下也不会提供x权限)。同样地,由于掩码的0011部分,chmod +x只会增加用户的+x权限,忽略组和其他权限,您需要使用chmod o+xchmod g+xchmod go+xchmod a+x来强制设置它们。

以下是模拟该行为的版本:

#!/usr/bin/env python3

import os
import stat

def get_umask():
    umask = os.umask(0)
    os.umask(umask)
    return umask

def chmod_plus_x(path):
    os.chmod(
        path,
        os.stat(path).st_mode |
        (
            (
                stat.S_IXUSR |
                stat.S_IXGRP |
                stat.S_IXOTH
            )
            & ~get_umask()
        )
    )

chmod_plus_x('.gitignore')

参阅:如何在Python中获取默认文件权限?

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


5
您也可以这样做。
>>> import os
>>> st = os.stat("hello.txt")

当前文件列表

$ ls -l hello.txt
-rw-r--r--  1 morrison  staff  17 Jan 13  2014 hello.txt

现在执行以下步骤。
>>> os.chmod("hello.txt", st.st_mode | 0o111)

你将在终端中看到这个。

ls -l hello.txt    
-rwxr-xr-x  1 morrison  staff  17 Jan 13  2014 hello.txt

您可以使用0o111进行按位或运算,使所有文件都可执行;使用0o222使所有文件可写入;使用0o444使所有文件可读取。


0
这将为文件打开所有可执行位: os.chmod("path", os.stat("path").st_mode | 0o111)

-1

在Python3中:

import os
os.chmod("somefile", 0o664)

记得添加0o前缀,因为权限设置是八进制整数,而Python自动将任何具有前导零的整数视为八进制。否则,您实际上正在传递os.chmod(“somefile”,1230),这是664的八进制。


3
这会将权限设置为绝对值,而不是像提问者要求的 chmod + 那样添加新权限到现有权限上。 - Ciro Santilli OurBigBook.com

-1

我们可以使用os.system()在Python中直接调用chmod +x命令。

import os
os.system("chmod +x somefile")

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