Finding the "root" of a directory - python

I am attempting to write a function in python that scans the contents of a directory at the script's level (once de-bugged I'll switch it to not needing to be at the same level but for this problem it's irrelevant) and recursively lists the paths to anything that is not a directory. The logic I am working with is:
If the parent "directory" is not a directory then it must be a file so print the path to it. Otherwise, for every "file" in that directory, if each "file" is not actually a directory, state the path to the file, and if the "file" is actually a directory, call the function again.
The environment I am using is as follows: I have the script at the same level as a directory named a, and inside a is a file d.txt, as well as another directory named b. Inside b is a file c.text. Per the way I would like this function to execute, first it should recognize that a is in fact a directory, and therefore begin to iterate over its contents. When it encounters d.txt, it should print out the path to it, and then when it encounters directory b it should begin to iterate over it's contents and thereby print the path to c.txt when it sees it. So in this example, the output of the script should be "C:\private\a\d.txt, C:\private\a\b\c.txt" but instead it is "C:\private\d.txt, C:\private\b". Here is the code thus far:
import os
def find_root(directory):
if not os.path.isdir(directory):
print(os.path.abspath(directory))
else:
for file in os.listdir(directory):
if not os.path.isdir(file):
print(os.path.abspath(file))
else:
find_root(file)
find_root('a')

[Python]: os.listdir(path='.'):
Return a list containing the names of the entries in the directory given by path.
but they are just basenames. So, in order for them to make sense, when you go a level deeper in the recursion either:
Prepend the "current" folder to their name
cd to each folder (and also, cd back when returning from recursion)
Here's your code modified to use the 1st approach:
import os
def find_root(path):
if os.path.isdir(path):
for item in os.listdir(path):
full_item = os.path.join(path, item)
if os.path.isdir(full_item):
find_root(full_item)
else:
print(os.path.abspath(full_item))
else:
print(os.path.abspath(path))
if __name__ == "__main__":
find_root("a")
Notes:
I recreated your folder structure
I renamed some of the variables for clarity
I reversed the negate conditions
Output:
c:\Work\Dev\StackOverflow\q47193260>"c:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" a.py
c:\Work\Dev\StackOverflow\q47193260\a\b\c.txt
c:\Work\Dev\StackOverflow\q47193260\a\d.txt

Related

shutil.move() creates duplicates and fails on subsequent calls

I finished writing a script which creates some files so I'm making a tidy() function which sorts these files in folders. The end result should look like this:
/Scripting
- Output
- script.py
/Scripting/Output
- Folder1
- Folder2
- Folder3
Each folder contains the necessary files
I managed to create the list of folders and get the files in them without any problem so I now have in /Project: script.py, folder1, folder2, etc... I copy pasted most of the code from the first part in order to move them into the Output folder. The following code is executed with every subfolder containing their respective files is located in the same directory as the script.
try:
os.mkdir('output')
except FileExistsError:
pass
for file in os.listdir():
if '.' not in file and file != 'output':
shutil.move(file, f'{os.getcwd()}/output/{file})
The problem is that if I look into my folder after running, I find the following directory tree:
/Output
- Folder1
- Folder1
- File1
- File2
I get a duplicate folder within that folder and I don't understand where it's coming from. If I try to call the script again, I get the error: shutil.Error destination path 'Scripting/output/folder1/fodler1' already exists
What am I doing wrong?
Edit:
Here's the new code:
try:
os.mkdir('output')
except FileExistsError:
pass
obj = os.scandir()
cwd = os.getcwd()
for entry in obj:
if entry.is_dir() and not entry.name.startswith('.'):
continue
shutil.move(entry.name, f'{cwd}/'/output/'{entry.name}')
This works the first time I run it, but breaks if I keep calling the script by giving me the same mistake as above. It creates folder1 within folder1 only on subsequent calls and I can't find a reason for it.
Found the answer mostly by trial and error. I initially chose to use shutil.move() because it replaces a file if it finds another one with the same name. However, it does not do this with directory. It will instead add to that path. /Scripting/Output/Folder1/ as a destination path for Folder1 would give an error when I run the script a second time so instead of replacing the folder, it simple adds it into its path which would then become /Scripting/Output/Folder1/Folder1/ while still adding the files to the initial path (it looks like it runs the shutil.move() on everything within that path). To fix this, use obj = os.scandir() with obj.is_dir() and obj.name to parse your folders. Either os.rmdir() the extra folder every time, or add the folders before adding the files. This is the code that worked for me:
cwd = os.getcwd()
try:
os.mkdir('output')
except:
pass
os.chdir('output')
for name in folder_names:
try:
os.mkdir(name)
except:
pass
os.chdir('..')
obj = os.scandir()
cwd = os.getcwd()
for f in obj:
if f.is_file():
if True:# depends on how your files are organized
shutil.move(f.name, f'{cwd}/output/folder1/{f.name}')
# Do this for every file

Moving files to a folder in python

I have a code that locates files in a folder for me by name and moves them to another set path.
import os, shutil
files = os.listdir("D:/Python/Test")
one = "one"
two = "two"
oney = "fold1"
twoy="fold2"
def findfile(x,y):
for file in files:
if x in file.lower():
while x in file.lower():
src = ('D:/Python/Test/'+''.join(file))
dest = ('D:/Python/Test/'+y)
if not os.path.exists(dest):
os.makedirs(dest)
shutil.move(src,dest)
break
findfile(one,oney)
findfile(two,twoy)
In this case, this program moves all the files in the Test folder to another path depending on the name, let's say one as an example:
If there is a .png named one, it will move it to the fold1 folder. The problem is that my code does not distinguish between types of files and what I would like is that it excludes the folders from the search.
If there is a folder called one, don't move it to the fold1 folder, only move it if it is a folder! The other files if you have to move them.
The files in the folder contain the string one, they are not called exactly that.
I don't know if I have explained myself very well, if something is not clear leave me a comment and I will try to explain it better!
Thanks in advance for your help!
os.path.isdir(path)
os.path.isdir() method in Python is used to check whether the specified path is an existing directory or not. This method follows symbolic link, that means if the specified path is a symbolic link pointing to a directory then the method will return True.
Check with that function before.
Home this helps :)

Python script that creates new file and returns list of files

I'm trying to create a python script called script.py with new_directory function that creates a new directory inside the current working directory, then creates a new empty file inside the new directory, and returns the list of files in that directory.
The output I get is ["script.py"] which looks correct but gives me this error:
RuntimeErrorElement(RuntimeError,Error on line 5:
directory = os.mkdir("/home/PythonPrograms")
FileExistsError: [Errno 17] File exists: '/home/PythonPrograms'
)
import os
def new_directory(directory, filename):
if os.path.isdir(directory) == False:
directory = os.mkdir("/home/PythonPrograms")
os.chdir("PythonPrograms")
with open("script.py", "w") as file:
pass
# Return the list of files in the new directory
return os.listdir("/home/PythonPrograms")
print(new_directory("PythonPrograms", "script.py"))
How do I correct and why is this wrong?
As others have said, it is hard to debug without the error. In the right cercumstances, your code will work without errors. As #Jack suggested, I suspect you're current directory is not /home. This means you've created a directory called PythonPrograms in /home directory. os.chdir("PythonPrograms") is trying to change the directory to <currentDirectory>/PythonPrograms, which doesn't exist.
I have tried to rework your code (without completely changing it), into something that should work in all cases. I think the lesson here is, work with the variables you already have (i.e. directory), rather than hardcoding it into the function.
import os
def new_directory(directory, filename):
if not os.path.isdir(directory):
# Create directory within current directory
# This is working off the relative path (from your current directory)
directory = os.mkdir(directory)
# Create file if does not exist
# this is a one-liner version of you with...pass statement
open(os.path.join(directory, filename), 'a').close()
# Return the list of files in the new directory
return os.listdir(directory)
print(new_directory("PythonPrograms", "script.py"))
I hope that helps.
I'm guessing the error you're getting is because you're not able to switch directories to PythonPrograms? This would be because your python current working directory does not contain it. If you more explicitly write out the directory you want to switch to, for example putting os.chdir("/home/PythonPrograms"), then it may work for you.
Ideally you should give us any stack traces or more info on the errors, though
I'm not sure why in your code you have with open("script.py", "w") as file: pass,
but here is mt way:
import os
os.mkdir('.\\Newfolder') # Create a new folder called Newfolder in the current directory
open('.\\Newfolder\\file.txt','w').close() # Create a new file called file.txt into Newfolder
print(os.listdir('.')) # Print out all the files in the current directory

Rename a directory with a filename inside with Python

I am trying to rename several directories with the name of the first file inside them.
I am trying to:
List the files inside a folder.
Identify the directories.
For each directory, access it, grab the name of the first file inside and rename the directory with such name.
This is what I got so far but it is not working. I know the code is wrong but before fixing the code I would like to know if the logic is right. Can anyone help please?
import os
for (root, dirs, files) in os.walk('.'):
print(f'Found directory: {dirpath}')
dirlist = []
for d_idx, d in enumerate(dirlist):
print(d)
filelist = []
for f_idex, f in enumerate(filelist):
files.append(f)[1]
print(f)
os.rename(d, f)
Thank you!
There are a few problems in your code:
You are renaming directories as you iterate them with os.walk. This is not a good idea, os.walk gives you a generator, meaning it creates elements as you iterate them, so renaming things within the loop will confuse it.
Both for d_idx, d in enumerate(dirlist): and for f_idex, f in enumerate(filelist): iterate over variables that are declared to be empty lists in the line before, so those loops don't do anything. Also, within the second one, files.append(f) would append f to the list files, but the [1] at the end means "get the second element (remeber Python indexing is 0-based) of the value returned by the append function" - but append does not return anything (it modifies the list, not returns a new list), so that would fail (and you are not using the value read by [1] anyway, so it would not do anything).
In os.rename(d, f), first, since the loops before do not ever run, d and f will not have a value, but also, assuming both d and f came from dirs and files, they would be given as paths relative to their parents, not to your current directory (.), so the renaming would fail.
This code should work as you want:
import os
# List of paths to rename
renames = []
# Walk current dir
for (root, dirs, files) in os.walk('.'):
# Skip this dir (cannot rename current directory)
if root == '.': continue
# Add renaming to list
renames.append((root, files[0]))
# Iterate renaming list in reverse order so deepest dirs are renamed first
for root, new_name in reversed(renames):
# Make new full dir name (relative to current directory)
new_full_name = os.path.join(os.path.dirname(root), new_name)
# Rename
os.rename(root, new_full_name)

program to print directory structure recursively in python not working

I have this directory structure:
test1
file1.txt
test2
file2.txt
test3
file3.txt
test4
file4.txt
my current code to print this directory levels is as follows:
import os
def printRootStructure(dirname,indent=0):
for i in range(indent):
print " ",
print dirname
if os.path.isdir(dirname):
for files in os.listdir(dirname):
printRootStructure(files,indent+1)
printRootStructure("test")
It currently prints as
test
file1.txt
test1
It is not proceeding to next level. any help here to troubleshoot?
Unless you have a specific reason to use recursion, it's simpler to use os.walk to traverse a directory structure.
import os
import os.path as P
for topdir, subdirs, files in os.walk(starting_point):
print " " * topdir.count(P.sep), P.basename(topdir)
for f in sorted(files):
print " " * (topdir.count(P.sep) + 1), f
I think you can fix this by passing the full path name into printRootStructure:
import os
def printRootStructure(dirname,indent=0):
for i in range(indent):
print " ",
print os.path.basename(dirname) # changed
if os.path.isdir(dirname):
for files in os.listdir(dirname):
printRootStructure(os.path.join(dirname,files),indent+1) # changed
As it was in your original code, you were passing just the last part (this is called the "basename") of each file into printRootStructure when you made the recursive calls.
Working directory and path names
Any time you start up a program on a modern computer, your program runs in a fixed location in the filesystem (this is called the "current working directory"). If you invoke a program from the commane-line, the current working directory is simply the path where you invoked the program's name. If you invoke a program by clicking something in a GUI environment, it can get more complicated, but the general behavior is the same: your program always runs in a specific working directory.
All path tests, and in particular os.path.isdir, are evaluated with respect to that working directory. So when you make your first recursive call in your example, you are testing os.path.isdir("test1"), which doesn't exist in the working directory -- it only exists inside "test" !
The fix is to pass the full path name into your recursive calls. Then, because your full name might be excessive when you print out the tree, I added a call to os.path.basename to print just the basename portion of each file.

Categories