python3 pathlib mkdir Permission denied - python-3.x

This code raised exception when I tried to create a sub dir ./test/123 under ./test/. And after examine the permission, I found that dir ./test created by this code has d-w----r--, which is strange...If I mkdir in the terminal, that dir will have drwxr-xr-x permission.
from pathlib import Path
if __name__ == '__main__':
p1 = Path('./test')
p1.mkdir(644, parents=True, exist_ok=True)
p2 = Path('./test/123')
p2.mkdir(644, parents=True, exist_ok=True)
File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/pathlib.py", line 1267, in mkdir
if not exist_ok or not self.is_dir():
File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/pathlib.py", line 1358, in is_dir
return S_ISDIR(self.stat().st_mode)
File "/usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/pathlib.py", line 1168, in stat
return self._accessor.stat(self)
PermissionError: [Errno 13] Permission denied: 'test/123'

Pathlib expects an octal integer instead of decimal. You can denote octal by preficing your mode bits 644 with 0o, i.e. 0o644. 644 decimal translates to 1204 in octal which imposes permissions you are seeing there.
Also, to traverse a directory structure you require both read and execute permissions on it, so I would recommend using 0o755 instead of 0o644.
The Unix command line chmod assumes octal whereas your python pathlib library does not. Hope this helps.

Related

Working with shutil and os: [Errno 2] No such file or directory 'folder'

import os, shutil
directory = 'C:\\Users\\MinJun\\Documents\\Python exercise solutions'
def move_files(_dir):
for file in os.listdir(_dir): #check every file in directory
if os.path.isdir(file): #if it is a folder, skip
continue
if file.endswith('.py'): #if file ends with .py, skip
continue
else: #move file to newfolder, (it will automatically create one)
shutil.move(file, directory.join('\\newfolder'))
move_files(directory)
Hello, I am trying to move files that are not folders or .py files to a folder that does not exist (but will be created with shutil.move). I get an error in the shutil module:
FileNotFoundError: [Errno 2] No such file or directory: 'graphics'
My folder 'graphics' is the first item in the directory.
Try
shutil.move(file, ''.join([directory, '\\newfolder'])
join does probably not work as you expected: https://docs.python.org/3.5/library/stdtypes.html#str.join
If you print(directory.join('\\newfolder')) you can see what it resulted in, and that this path for sure does not exist.
There is also os.path.join(path, *paths), which is sort of a "path-aware" string joining function.

open() doesn't work when imported

I have a file in my site-packages directory called wordlist.py that consists of just one line:
f = open("words.txt")
There is a file called words.txt in the same directory. When I run wordlist.py it works fine. However, whenever I use import wordlist, I get an error:
FileNotFoundError: [Errno 2] No such file or directory: 'words.txt'
I am using IDLE for Python 3.4
if you use relative paths for file or directory names python will look for them (or create them) in your current working directory (the $PWD variable in bash).
if you want to have them relative to the current python file, you can use (python 3.4)
import pathlib
HERE = Path(__file__).parent.absolute()
WORDS_PATH = HERE / '../path/to/words.txt'
with WORDS_PATH.open() as file_pointer:
'do something with file_pointer...'

In Python, list certain type of file in a directory on Linux

In my directory, there are a kind of type of file end in .log file.
In ordinary, I use ls .*log commands to list all files.
However, I wanna to use Python code to handle with it. There are two ways I've tried.
First:
import subprocess
ls_al = subprocess.check_output(['ls','.*log'])
but it returns ls: .*log: No such file or directory
Second:
import subprocess
ls_al = subprocess.check_Popen(['ls','.*log'],stdout=subprocess.PIPE)
ls = ls_al.stdout.read().strip()
but those two didn't work.
Can anyone help with this?
Globbing patterns are expanded by the shell, but you are running the command directly. You'd have to run the command through the shell:
ls_al = subprocess.check_output('ls *.log', shell=True)
where you pass in the full command line to the shell as a string (and use the correct glob syntax).
Demo (using *.py):
>>> subprocess.check_output(['ls', '*.py'])
ls: *.py: No such file or directory
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/mj/Development/Library/buildout.python/parts/opt/lib/python2.7/subprocess.py", line 575, in check_output
raise CalledProcessError(retcode, cmd, output=output)
subprocess.CalledProcessError: Command '['ls', '*.py']' returned non-zero exit status 1
>>> subprocess.check_output('ls *.py', shell=True)
'calc.py\ndAll.py\nexample.py\ninplace.py\nmyTests.py\ntest.py\n'
Note that the correct way in Python is to use os.listdir() with manual filtering, filter with the fnmatch module, or use the glob module to list and filter together:
>>> import glob
>>> glob.glob('*.py')
['calc.py', 'dAll.py', 'example.py', 'inplace.py', 'myTests.py', 'test.py']
.*log seems like regular expression, not globbing pattern. Do you mean *.log? (need shell=True argument to make shell do glob expansion)
BTW, glob.glob('*.log') is more preferable way if you want list of file paths.
Rather than run an external command, you could use Python's os module to get the files in the directory. Then the re module can be used to create a regular expression to filter for your log files. I think this would be a more pythonic approach. It should also work on multiple platforms without modification. Note that in the code below I'm assuming your log files all end with '.log'; if you need something else you'll need to tinker with the regex.
import os
import re
import sys
the_dir = sys.argv[1]
all_files = os.listdir(the_dir)
log_files = []
log_pattern = re.compile('.*\.log')
for fn in all_files:
if re.match(log_pattern, fn):
log_files.append(fn)
print log_files
Why not use glob?
$ ls
abc.txt bar.log def.txt foo.log ghi.txt zoo.log
$ python
>>> import glob
>>> for logfile in glob.glob('*.log'):
... print(logfile)
...
bar.log
foo.log
zoo.log
>>>

How to remove setgid (linux/unix)?

I just changed my file permissions using $ sudo chmod g+s filename and my file permissions turned from drwxr-xr-x to drwxr-sr-x. How do I remove it?
Change the + for adding a permission into a - to remove it:
sudo chmod g-s filename
If you want to do this programatically, you’ll need to use some bitwise operators. Normally it’s
mode_without_suid = bitwise_and(existing_mode, bitwise_not(S_ISUID))
where S_ISUID is 0o4000, a constant that uses mode bits above the typical rwx ones of something like 0644.
For example, in python
import os
import stat
def mode_details(m):
return f"mode={oct(m)} = {stat.filemode(m)}"
mode = os.stat('foo').st_mode
print("old mode", mode_details(mode))
new_mode = mode & ~stat.S_ISUID
os.chmod('foo', new_mode)
print("new mode", mode_details(new_mode))
which prints
old mode mode=0o104654 = -rwSr-xr--
new mode mode=0o100654 = -rw-r-xr--
To remove setgid the numerical way the command is
sudo chmod 0664 $filename
The assumption here is the permission on file is 664 and we are not changing it. The left most bit in the above command represents setuid(4),setgid(2) and sticky(1). Now to represent these symbolically setuid is u+s, setgid is g+s and sticky is o+t
Example 1:-chmod u+s filename
This will setuid for the filename mentioned that is rwsr_xr_x
Example 2: chmod 2770 directory
This will set gid for the directory mentioned that is rwxr_sr_x
Regarding: "you can set (but not clear) the bits with a numeric mode"
On RHEL 7 chmod 0644 $filename did not remove the setuid(4),setgid(2) or sticky(1).
However precedeing with an extra 0 did the trick:
chmod 00644 $filename
Well would just like to add few points to clarify the approach of working with the numerical way for both files and directories.
Adding individual special permissions for either user/group/others.
chmod "X"755 file
Where X is the specific octal numeric mode for special permissions.
If you want to add multiple special permissions at a time, e.g. for both suid(4) and sgid(2) i.e. 4+2=6.
chmod "6"755 file
for suid(4), sgid(2) and sticky bit(1), i.e. 4+2+1=7
chmod "7"755 file
Deleting all special permissions (only applicable for a file)
chmod 00"0"755 file
Well, the trailing zeros before 4 digits doesn't add any values while changing the permission for a file but it does add values while changing permission for a directory.
The above numeric code will change the permission to 755 from 7755 only for a file but if you do the same for a directory it will be 6755 as it will only remove the sticky bit for others.
To remove all the special permissions for a directory.
chmod "000"755 file
Similarly, to remove suid permission and having sgid(2) and sticky bit(1) i.e. 2+1=3.
chmod 00"3"755 file
And solution using letters(r,w,x,X,s,,t) and operators(+/-) were already discussed and approved in the earlier answers.
sgid with number
#chmod 2(permission) (directory name) = for adding
#chmod 0(permission) (directory name) = for removing
sgid with word
#chmod g+s directory name = for adding
#chmod g-s directory name = for removing

Delete file with odd character in filename

I cannot delete a file that is copy of a backup of a backup... I don't remember all the filesystem character set it has passed by.
Anyway, today here's the file:
nas# ls -al
ls: cannot access Sécurité: No such file or directory
total 32
drwx------ 4 sambacam sambacam 20480 Jun 5 01:38 .
drwxr-xr-x 3 sambacam sambacam 12288 Jun 5 01:38 ..
d????????? ? ? ? ? ? S??curit??
nas# cd S*
cd: 13: can't cd to Sécurité
nas# rm "Sécurité"
rm: cannot remove `S\303\251curit\303\251': No such file or directory
nas# rm S*
rm: cannot remove `S\303\251curit\303\251': No such file or directory
nas#
I even tried to code in Python without success:
nas# python
Python 2.5.2 (r252:60911, Jan 24 2010, 20:48:41)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> d=os.listdir('.')
>>> d
['S\xc3\xa9curit\xc3\xa9']
>>> d[0]
'S\xc3\xa9curit\xc3\xa9'
>>> os.remove(d[0])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 2] No such file or directory: 'S\xc3\xa9curit\xc3\xa9'
>>>
Any idea?
I already ran fsck to check for inconsistencies.
I think you've got worse problems:
d????????? ? ? ? ? ? S??curit??
This means that ls(1) was unable to find permissions, link count, owner, group, size, or mtime of your file. All it has is a filename.
This could happen if the directory structure points to a file, but the inode for that file has gone missing. I would hope a fsck would find it and clean up the directory entry, but if that hasn't happened, you might not be able to ever empty this directory on this filesystem. (You could move it wherever you wanted, even into the /lost+found, and not be bothered by it again...)
Perhaps the debugfs(8) tool would be useful in learning more?
Have you tried with the inode number trick? Do:
ls -ilb
The first number in that list is the inode number. The -b switch makes ls not try to print non-printable chars. Once you have the inode number from the file, try:
find . -inum the_number_from_above -exec rm -i {} \;
(BTW: that's UTF-8 encoding.)
I'm not sure it will work though. The fact that ls isn't finding the file's metadata (timetamps and permission bits) looks like filesystem corruption.

Resources