Как сделать простой «chmod + x» из Python?

120

Я хочу создать файл из исполняемого скрипта python.

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

похоже os.chmod, не «добавляет» разрешения, как это chmodделает unix . Если последняя строка закомментирована, файл имеет файловый режим -rw-r--r--, а без комментариев - файловый режим ---x------. Как я могу просто добавить u+xфлаг, сохранив при этом остальные режимы?

priestc
источник

Ответы:

198

Используйте os.stat()для получения текущих разрешений, используйте |для или биты вместе и используйте os.chmod()для установки обновленных разрешений.

Пример:

import os
import stat

st = os.stat('somefile')
os.chmod('somefile', st.st_mode | stat.S_IEXEC)
Игнасио Васкес-Абрамс
источник
2
Это только делает его исполняемым пользователем. Плакат спрашивал о "chmod + x", что делает его исполняемым для всех (пользователь, группа, мир)
eric.frederich
35
Используйте следующее, чтобы сделать его доступным для всех ... stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH. Примечание: это значение совпадает с восьмеричным числом 0111, поэтому вы можете просто выполнить st.st_mode | 0111
eric.frederich
1
Мой ответ ниже копирует биты R в X, как и следовало ожидать, скажем, от компилятора.
Джонатон Рейнхарт
Я бы сделал STAT_OWNER_EXECUTABLE = stat.S_IEXECи использовал удобочитаемую локальную константу вместо тарабарщины.
ThorSummoner
вот непитонический ответ, который может быть немного более читаемым: subprocess.check_call(['chmod', '+x', 'somefile'])и вам будет проще выполнять такие операции, как a+rx.
Тревор Бойд Смит,
20

Для инструментов, которые генерируют исполняемые файлы (например, скрипты), может быть полезен следующий код:

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

Это заставляет его (более или менее) уважать то, umaskчто было в силе при создании файла: Executable устанавливается только для тех, кто может читать.

Использование:

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 изменены восьмеричные литералы. Вместо этого 0444вы должны использовать 0o444. Или, если вы хотите поддержать обоих, просто напишите 292.
Кевин
1
@Kevin Похоже, что новый синтаксис был поддержан Python 2.6, поэтому кажется разумным его использовать. (В качестве ориентира совместимости CentOS 6 поставляется с Python 2.6).
Джонатон Рейнхарт
2
Я не знал, что Python 3 удалил традиционные восьмеричные литералы. Так что спасибо вам за это.
Джонатон Рейнхарт
13

Если вы знаете, какие разрешения вам нужны, следующий пример может помочь вам упростить задачу.

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
4

Вы также можете сделать это

>>> 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 сделать все доступными для чтения.

ncmathsadist
источник
2

Уважай umaskкакchmod +x

man chmodговорит, что если augoне дается как в:

chmod +x mypath

then aиспользуется, но с umask:

Комбинация букв ugoa определяет, у каких пользователей будет изменен доступ к файлу: пользователь, которому он принадлежит (u), другие пользователи в группе файла (g), другие пользователи, не входящие в группу файла (o), или все пользователи (а). Если ничего из этого не задано, эффект будет таким, как если бы был задан (a), но биты, которые установлены в umask, не затронуты.

Вот версия, которая точно имитирует это поведение:

#!/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.

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
1

В python3:

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

Не забудьте добавить 0oпрефикс, поскольку разрешения устанавливаются как восьмеричное целое число, а Python автоматически обрабатывает любое целое число с нулем в начале как восьмеричное. В противном случае вы os.chmod("somefile", 1230)действительно проходите , что является восьмеричным от 664.

Funkid
источник
1
Это устанавливает абсолютное значение разрешений, но не делает так, chmod +как просит OP, который должен добавлять новые разрешения к существующим.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
0

Если вы используете Python 3.4+, вы можете использовать удобный pathlib стандартной библиотеки .

Его класс Path имеет встроенные методы chmod и stat .

from pathlib import Path


f = Path("/path/to/file.txt")
f.chmod(f.stat().st_mode | stat.S_IEXEC)
cs01
источник