pathlib mkdir creates a folder by filename - python-3.x

I have the following preexisting folder in my machine
D:\scripts\myfolder
I want my script to create a folder named logs and create a file log.txt in it. So the path would look like
D:\scripts\myfolder\logs\somelog.txt
So I used
p = pathlib.Path("D:\scripts\myfolder\logs\somelog.txt")
p.mkdir(parents=True, exisit_ok=True)
Now
print(p.parents[0]) ==> D:\scripts\myfolder\logs
print(p.parents[1]) ==> D:\scripts\myfolder
print(p.parents[2]) ==> D:\scripts
So, as per Path.mkdir documentation
p.mkdir(parents=True, exisit_ok=True) should create the folders logs, myfolder or scripts and so on if they don't exist.
But it creates a folder by the name some.txt inside logs folder, although it is none of the parents. Why is that so?
I understand that the workaround is to use pathlib.Path("D:\scripts\myfolder\logs")

The entire point of mkdir is to create the directory pointed to by its argument. Passing in parents=True creates the parent folders in addition.
Create a new directory at this given path. [...] If parents is true, any missing parents of this path are created as needed; [1]
If you want to ensure the containing directory exists, create the parent of your path:
p = pathlib.Path("D:\scripts\myfolder\logs\somelog.txt")
p.parent.mkdir(parents=True, exist_ok=True)

That's the way Pathlib.mkdir works. It can't tell if the final component should be a file or a directory. parents=True means to create also parents, not only parents. If the final path component is always a file, you could avoid it like
p.parents[0].mkdir(parents=True)

Related

Zip compress without root folders

My problem is that I have to generate a zip file using the linux zip console command. My command is as follows:
zip -r /folder1/folder2/EXP_45.zip /folder1/folder2/EXP_45/
That returns a correct zip only that includes the root folders I want:
Returns
EXP_45.zip
-folder1
--folder2
---EXP_45
...
I want
EXP_45.zip
-EXP_45
...
EXP_45 is a folder that can contain files and folders and they must be present in the zip. I just want the tree structure to start with the EXP_45 folder.
Is there any solution?
The reason why I need it to be a single command is that it is an action of a job in a PL SQL function like:
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
JOB_NAME=>'compress_files', --- job name
JOB_ACTION=>'/usr/bin/zip', --- executable file with path
JOB_TYPE=>'executable', ----- job type
NUMBER_OF_ARGUMENTS=>4, -- parameters in numbers
AUTO_DROP =>false,
CREDENTIAL_NAME=>'credentials' -- give credentials name which you have created before "credintial"
);
dbms_scheduler.set_job_argument_value('compress_files',1,'-r');
dbms_scheduler.set_job_argument_value('compress_files',2,'-m');
dbms_scheduler.set_job_argument_value('compress_files',3,'/folder1/folder2/EXP_45.zip');
dbms_scheduler.set_job_argument_value('compress_files',4,'/folder1/folder2/EXP_45/');
DBMS_SCHEDULER.RUN_JOB('compress_files');
END;
I haven't been able to find a solution to this problem using zip but I have found it using jar. The command would be:
jar cMf /folder1/folder2/EXP_45.zip -C /folder1/folder2/EXP_45 .
Also, the solution using a job in pl sql in case it works for someone would be:
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
JOB_NAME=>'compress_files', --- job name
JOB_ACTION=>'/usr/bin/jar', --- executable file with path
JOB_TYPE=>'executable', ----- job type
NUMBER_OF_ARGUMENTS=>5, -- parameters in numbers
AUTO_DROP =>false,
CREDENTIAL_NAME=>'credentials' -- give credentials name which you have created before "credintial"
);
dbms_scheduler.set_job_argument_value('compress_files',1,'cMf');
dbms_scheduler.set_job_argument_value('compress_files',2,'/folder1/folder2/EXP_45.zip');
dbms_scheduler.set_job_argument_value('compress_files',3,'-C');
dbms_scheduler.set_job_argument_value('compress_files',4,'/folder1/folder2/EXP_45');
dbms_scheduler.set_job_argument_value('compress_files',5,'.');
DBMS_SCHEDULER.RUN_JOB('compress_files');
END;
You want to use the -j (or --junk-paths) option when you are creating the zip file. Below is from the zip man page.
-j
--junk-paths
Store just the name of a saved file (junk the path), and do not store directory names.
By default, zip will store the full path (relative to the current directory).
Update following Question Clarification
Why not put the equivalent to the code below in a shell script & get the SQL function to invoke that? You just need to pass the directory name to cd into and the name of the output zip.
cd folder1/folder2
zip -r /tmp/EXP_45.zip EXP_45

The system cannot find the file specified - WinError 2

Upon looping a directory to delete txt files ONLY - a message is returned indicating The System cannot find the file specified: 'File.txt'.
I've made sure the txt files that I'm attempting to delete exist in the directory I'm looping. I've also checked my code and to make sure it can see my files by printing them in a list with the print command.
import os
fileLoc = 'c:\\temp\\files'
for files in os.listdir(fileLoc):
if files.endswith('.txt'):
os.unlink(files)
Upon initial execution, I expected to see all txt files deleted except for other non-txt files. The actual result was an error message "FileNotFoundError: [WinError 2] The system cannot find the file specified: 'File.txt'.
Not sure what I'm doing wrong, any help would be appreciated.
It isn't found because the the path you intended to unlink is relative to fileLoc. In fact with your code, the effect is to unlink the file relative to the current working directory. If there were *.txt files
in the cwd then the code would have unfortunate side-effects.
Another way to look at it:
Essentially, by analogy, in the shell what you're trying to do is equivalent to this:
# first the setup
$ mkdir foo
$ touch foo/a.txt
# now your code is equvalent to:
$ rm *.txt
# won't work as intended because it removes the *.txt files in the
# current directory. In fact the bug is also that your code would unlink
# any *.txt files in the current working directory unintentionally.
# what you intended was:
$ rm foo/*.txt
The missing piece was the path to the file in question.
I'll add some editorial: The Old Bard taught us to "when in doubt, print variables". In other words, debug it. I don't see from the OP an attempt to do that. Just a thing to keep in mind.
Anyway the new code:
Revised:
import os
fileLoc = 'c:\\temp\\files'
for file in os.listdir(fileLoc):
if file.endswith('.txt'):
os.unlink(os.path.join(fileLoc,file))
The fix: os.path.join() builds a path for you from parts. One part is the directory (path) where the file exists, aka: fileLoc. The other part is the filename, aka file.
os.path.join() makes a whole valid path from them using whatever OS directory separator is appropriate for your platform.
Also, might want to glance through:
https://docs.python.org/2/library/os.path.html

Creating a directory tree and feed it as an input to create the same tree again

I am trying to get the directory tree only from a root folder in a server. Then feed the output to another program so it will create the same structure to another server.
The hard way is to create a JSON like structure of tree and then parse it to another server and create folders.
Is there any pythonic way to do this?
Why JSON? You could create just list of directories, e.g. with this script:
import os
def print_dir(path):
with os.scandir(path) as it:
for entry in it:
if entry.is_dir():
d = os.path.join(path, entry.name)
yield d
yield from print_dir(d)
for d in print_dir('/'):
print(d)
This prints whole directory tree from root ('/'):
/lib
/lib/crda
/lib/crda/pubkeys
/lib/terminfo
/lib/terminfo/m
/lib/terminfo/c
/lib/terminfo/x
/lib/terminfo/E
...etc.
You then send this list to the server, where you will read it line by line and run mkdir with -p argument (create parents, no error if existing, make parent directories as needed).

shutil.make_archive not zipping to correct destination

As per the code below I am having issues with the zipping a directory using the python 3 shutil.make_archive function. The .testdir will be zipped but it is being zipped in /home/pi, instead of /home/pi/Backups.
zip_loc = '/home/pi/.testdir'
zip_dest = '/home/pi/Backups/'
shutil.make_archive(zip_loc, 'zip', zip_dest)
Could anyone explain what I am doing wrong?
Reading the docs here I came up with:
zip_loc = '/home/pi/.testdir'
zip_dest = '/home/pi/Backups/'
shutil.make_archive(base_dir=zip_loc, root_dir=zip_loc, format='zip', base_name=zip_dest)
From the docs:
base_name is the name of the file to create, including the path, minus any format-specific extension.
root_dir is a directory that will be the root directory of the archive; for example, we typically chdir into root_dir before creating the archive.
base_dir is the directory where we start archiving from; i.e. base_dir will be the common prefix of all files and directories in the archive.
root_dir and base_dir both default to the current directory.
Before to write the archive, move to the good directory :
old_path = os.getcwd()
os.chdir(path)
-> write the archive
After writing the archive move back to old directory :
os.chdir(old_path)

ParaView get file path

I am opening some VTU files from Directory X and there are other output files in that directory (for example log.txt) that I want to open via a plugin. If I do a os.getcwd() I end up in ParaViews installation directory. What I want is the directory of the VTU files I loaded BEFORE applying the plugin... So basically the start Point of the Pipline.
You could do something like this to get the reader
myreader = FindSource('MyReader')
then get the file name via the FileName attribute
myreader.FileName

Resources