Python module os.chmod(file, 664) does not change the permission to rw-rw-r-- but -w--wx----
PythonFilePermissionsChmodPython Problem Overview
Recently I am using Python module os, when I tried to change the permission of a file, I did not get the expected result. For example, I intended to change the permission to rw-rw-r--,
os.chmod("/tmp/test_file", 664)
The ownership permission is actually -w--wx--- (230)
--w--wx--- 1 ag ag 0 Mar 25 05:45 test_file
However, if I change 664 to 0664 in the code, the result is just what I need, e.g.
os.chmod("/tmp/test_file", 0664)
The result is:
-rw-rw-r-- 1 ag ag 0 Mar 25 05:55 test_file
Could anybody help explaining why does that leading 0 is so important to get the correct result?
Python Solutions
Solution 1 - Python
Found this on a different forum
> If you're wondering why that leading zero is important, it's because > permissions are set as an octal integer, and Python automagically > treats any integer with a leading zero as octal. So os.chmod("file", > 484) (in decimal) would give the same result.
What you are doing is passing 664
which in octal is 1230
In your case you would need
os.chmod("/tmp/test_file", 436)
[Update] Note, for Python 3 you have prefix with 0o (zero oh). E.G, 0o666
Solution 2 - Python
So for people who want semantics similar to:
$ chmod 755 somefile
Use:
$ python -c "import os; os.chmod('somefile', 0o755)"
If your Python is older than 2.6:
$ python -c "import os; os.chmod('somefile', 0755)"
Solution 3 - Python
Use permission symbols (stat.S_I*
) instead of raw octal numbers
Your problem would have been avoided if you had used the more semantically named permission symbols rather than raw magic numbers, e.g. for 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
)
This is documented at https://docs.python.org/3/library/os.html#os.chmod and the names are the same as the POSIX C API which is also present at man 2 stat
and man 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
Another advantage is the greater portability as mentioned in the docs:
> Note: Although Windows supports chmod()
, you can only set the file’s read-only flag with it (via the stat.S_IWRITE
and stat.S_IREAD
constants or a corresponding integer value). All other bits are ignored.
chmod +x
is demonstrated at: https://stackoverflow.com/questions/12791997/how-do-you-do-a-simple-chmod-x-from-within-python/55591471#55591471
Tested in Ubuntu 16.04, Python 3.5.2.
Solution 4 - Python
leading 0
means this is octal constant, not the decimal one. and you need an octal to change file mode.
permissions are a bit mask, for example, rwxrwx---
is 111111000
in binary, and it's very easy to group bits by 3 to convert to the octal, than calculate the decimal representation.
0644
(octal) is 0.110.100.100
in binary (i've added dots for readability), or, as you may calculate, 420
in decimal.
Solution 5 - Python
If you have desired permissions saved to string then do
s = '660'
os.chmod(file_path, int(s, base=8))
Solution 6 - Python
@mc.dev's answer was the best answer here I ended up leveraging that to make the below function wrapper for reuse. Thanks for the share.
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://stackoverflow.com/a/60052847/1621381
"""
os.chmod(file_path, int(str(perms), base=8))
Solution 7 - Python
Using the stat.* bit masks does seem to me the most portable and explicit way of doing this. But on the other hand, I often forget how best to handle that. So, here's an example of masking out the 'group' and 'other' permissions and leaving 'owner' permissions untouched. Using bitmasks and subtraction is a useful pattern.
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)