Using Python to execute a command on every file in a folder - python

I'm trying to create a Python script that would :
Look into the folder "/input"
For each video in that folder, run a mencoder command (to transcode them to something playable on my phone)
Once mencoder has finished his run, delete the original video.
That doesn't seem too hard, but I suck at python :)
Any ideas on what the script should look like ?
Bonus question : Should I use
os.system
or
subprocess.call
?
Subprocess.call seems to allow for a more readable script, since I can write the command like this :
cmdLine = ['mencoder',
sourceVideo,
'-ovc',
'copy',
'-oac',
'copy',
'-ss',
'00:02:54',
'-endpos',
'00:00:54',
'-o',
destinationVideo]
EDIT : Ok, that works :
import os, subprocess
bitrate = '100'
mencoder = 'C:\\Program Files\\_utilitaires\\MPlayer-1.0rc2\\mencoder.exe'
inputdir = 'C:\\Documents and Settings\\Administrator\\Desktop\\input'
outputdir = 'C:\\Documents and Settings\\Administrator\\Desktop\\output'
for fichier in os.listdir(inputdir):
print 'fichier :' + fichier
sourceVideo = inputdir + '\\' + fichier
destinationVideo = outputdir + '\\' + fichier[:-4] + ".mp4"
commande = [mencoder,
'-of',
'lavf',
[...]
'-mc',
'0',
sourceVideo,
'-o',
destinationVideo]
subprocess.call(commande)
os.remove(sourceVideo)
raw_input('Press Enter to exit')
I've removed the mencoder command, for clarity and because I'm still working on it.
Thanks to everyone for your input.

To find all the filenames use os.listdir().
Then you loop over the filenames. Like so:
import os
for filename in os.listdir('dirname'):
callthecommandhere(blablahbla, filename, foo)
If you prefer subprocess, use subprocess. :-)

Use os.walk to iterate recursively over directory content:
import os
root_dir = '.'
for directory, subdirectories, files in os.walk(root_dir):
for file in files:
print os.path.join(directory, file)
No real difference between os.system and subprocess.call here - unless you have to deal with strangely named files (filenames including spaces, quotation marks and so on). If this is the case, subprocess.call is definitely better, because you don't need to do any shell-quoting on file names. os.system is better when you need to accept any valid shell command, e.g. received from user in the configuration file.

The new recommend way in Python3 is to use pathlib:
from pathlib import Path
mydir = Path("path/to/my/dir")
for file in mydir.glob('*.mp4'):
print(file.name)
# do your stuff
Instead of *.mp4 you can use any filter, even a recursive one like **/*.mp4. If you want to use more than one extension, you can simply iterate all with * or **/* (recursive) and check every file's extension with file.name.endswith(('.mp4', '.webp', '.avi', '.wmv', '.mov'))

Python might be overkill for this.
for file in *; do mencoder -some options "$file"; rm -f "$file" ; done
The rm -f "$file" deletes the files.

AVI to MPG (pick your extensions):
files = os.listdir('/input')
for sourceVideo in files:
if sourceVideo[-4:] != ".avi"
continue
destinationVideo = sourceVideo[:-4] + ".mpg"
cmdLine = ['mencoder', sourceVideo, '-ovc', 'copy', '-oac', 'copy', '-ss',
'00:02:54', '-endpos', '00:00:54', '-o', destinationVideo]
output1 = Popen(cmdLine, stdout=PIPE).communicate()[0]
print output1
output2 = Popen(['del', sourceVideo], stdout=PIPE).communicate()[0]
print output2

Or you could use the os.path.walk function, which does more work for you than just os.walk:
A stupid example:
def walk_func(blah_args, dirname,names):
print ' '.join(('In ',dirname,', called with ',blah_args))
for name in names:
print 'Walked on ' + name
if __name__ == '__main__':
import os.path
directory = './'
arguments = '[args go here]'
os.path.walk(directory,walk_func,arguments)

I had a similar problem, with a lot of help from the web and this post I made a small application, my target is VCD and SVCD and I don't delete the source but I reckon it will be fairly easy to adapt to your own needs.
It can convert 1 video and cut it or can convert all videos in a folder, rename them and put them in a subfolder /VCD
I also add a small interface, hope someone else find it useful!
I put the code and file in here btw: http://tequilaphp.wordpress.com/2010/08/27/learning-python-making-a-svcd-gui/

Related

How to remove dots from a filename without removing them from the extension?

I am iterating through a folder that has files of different extensions. I want to remove dots ('.') from the filename, but not the extension. If I simply do this:
def filename_replacer(file_name):
extension = file_name[-4:]
raw_name = file_name[:-4]
new_name = raw_name.replace(".", "_")
new_name = new_name + extension
return new_name
file = "Remove.TheDotReport.xlsx"
cleaned_file = filename_replacer(file)
It comes out incorrect (ex: Remove_TheDotReport_xlxs). How can I consistently remove the dots in the file without messing up the extension?
Use os.path.splitext to get the extension out first.
import os
def filename_replacer(filename):
fname, fext = os.path.splitext(filename)
return fname.replace(".", "_") + fext
Since Python 3.4, a convenient way for working with paths is the pathlib built-in library. It treats paths as objects instead of simple strings.
Since Python 3.9, a new method was added - with_stem - which only changes the name of the file without the extension. So this can be done quite easily:
from pathlib import Path
path = Path("Remove.TheDotReport.xlsx")
print(path.with_stem(path.stem.replace('.', '_')))
# Remove_TheDotReport.xlsx
For older versions, you can still use the with_name method with a bit more work (adding the extension separately):
from pathlib import Path
path = Path("Remove.TheDotReport.xlsx")
print(path.with_name(path.stem.replace('.', '_') + path.suffix))
# Remove_TheDotReport.xlsx
This is for a bash script to remove dots from the filename and keep the extension dot if you want to change for anyone file ('dir /b *.filename') give filename here.
#echo off
for /f "delims=" %%a in ('dir /b *') do call :dot "%%a"
pause
goto :EOF
:dot
set "var=%~n1"
set "var=%var:.= %"
ren %1 "%var%%~x1"

how to check path/file exists or not in python?

I have two file in directory abc
test.py
hello.txt
File test.py:
import os.path
if os.path.exists('hello.txt'):
print('yes')
else:
print('no')
when execute test.py in same directory, the output is, as I'd expect, 'yes'
abc > python test.py
output: yes
but when try to execute form other directory
~ > python ~/Desktop/abc/test.py
output: no
how to correct this
# the real case
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
it works when executing within directory abc but fails form outside.
thanks, everyone, finally I found the solution, never thought that would be easy.... just change the working directory and voila๐ŸŽ‰ its work๐Ÿ™‚๐Ÿ™‚ใ€€ใ€€ใ€€
import os
...
script_path = os.path.dirname(os.path.realpath(__file__))
os.chdir(script_path)
...
...
Do a complete path, the tilda
~
specifies from where you "are right now"
to correctly specify it do the complete path. Easiest way to do this is go to file explorer, right click on the file, and press copy path. This should get you the complete path to the file which can be specified anywhere.
Please let me know if this helped!
In this case you need to search the file after walking through the directories and reading the contents.
You might consider os.scandir() to walk through the directories [python 3.5].
https://www.python.org/dev/peps/pep-0471/
Sample:
def find_file(path: str) -> str:
for entry in scandir(path):
if entry.is_file() and entry.name == 'token.pickle':
return_path = f'{path}\{entry.name}'
yield return_path
elif entry.is_dir():
yield from find_file(entry.path)
if __name__ == '__main__':
for path in find_file('.'):
with open(path, 'rb') as token:
creds = pickle.load(token)
well, if you do not know the complete path, this is much more difficult IMHO. I don't have any good, pythonic idea to do that!
To search for the file within your whole PC, use subprocess module and execute "find" command on linux (you're on linux, right?), catch the output, and ask if your file is there:
import subprocess
file_name = "test.txt"
sp = subprocess.Popen(["find", '/', "-name", file_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = sp.communicate()
found = False
for line in output:
if file_name.encode() in line:
found = True
print("Found:", found)
NOTE: to delimit the search replace "/" by the parent folder you expect the file to be in.
EDIT: On windows, though I could not test it, the command would be: "dir /s /p hello.txt", so the subprocess call would look like this: sp = subprocess.Popen(["cmd", "/c", 'dir', '/s', '/p', 'Links'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
I see you already post an answer to your own question here
Anyway, I wanted to advice you there's no need to use os.chdir() for the purpose here really, simply do it like this:
# the real case
path_to_your_file = os.path.join(os.path.dirname(os.path.realpath(__file__)),"token.pickle")
if os.path.exists(path_to_your_file):
with open(path_to_your_file, 'rb') as token:
...
P.S.
If you're wondering, there's several good reason to prefer using os.path.join() over manual string concatenation, the main one being it makes your coding platform independent in the first place

Python: Trying to put the contents of a folder into a text file:

I'm in the process of writing a python script that takes two arguments that will allow me to output the contents of a folder to a text file for me to use for another process. The snippet of I have is below:
#!/usr/bin/python
import cv2
import numpy as np
import random
import sys
import os
import fileinput
#Variables:
img_path= str(sys.argv[1])
file_path = str(sys.argv[2])
print img_path
print file_path
cmd = 'find ' + img_path + '/*.png | sed -e "s/^/\"/g;s/$/\"/g" >' + file_path + '/desc.txt'
print "command: ", cmd
#Generate desc.txt file:
os.system(cmd)
When I try and run that from my command line, I get the following output, and I have no idea how to fix it.
sh: 1: s/$//g: not found
I tested the command I am using by running the following command in a fresh terminal instance, and it works out fine:
images/*.png | sed -e "s/^/\"/g;s/$/\"/g" > desc.txt
Can anyone see why my snippet isn't working? When I run it, I get an empty file...
Thanks in advance!
its not sending the full text for your regular expression through to bash because of how python processes and escapes string content, so the best quickest solution would be to just manually escape the back slashes in the string, because python thinks they currently are escape codes. so change this line:
cmd = 'find ' + img_path + '/*.png | sed -e "s/^/\"/g;s/$/\"/g" >' + file_path + '/desc.txt'
to this:
cmd = 'find ' + img_path + '/*.png | sed -e "s/^/\\"/g;s/$/\\"/g" >' + file_path + '/desc.txt'
and that should work for you.
although, the comment on your question has a great point, you could totally just do it from python, something like:
import os
import sys
def main():
# variables
img_path= str(sys.argv[1])
file_path = str(sys.argv[2])
with open(file_path,'w') as f:
f.writelines(['{}\n'.format(line) for line in os.listdir(img_path) if line.endswith('*.png')])
if __name__ == "__main__":
main()
I fully agree with Kyle. My recommendation is to do using only python code better than call bash commands from your code. Here it is my recommended code, it is longer and not as optimal than the aforementioned one, but IMHO it is a more easy to understand solution.
#!/usr/bin/python
import glob
import sys
import os
# Taking arguments
img_path = str(sys.argv[1])
file_path = str(sys.argv[2])
# lets put the target filename in a variable (it is better than hardcoding it)
file_name = 'desc.txt'
# folder_separator is used to define how your operating system separates folders (unix / and windows \)
folder_separator = '\\' # Windows folders
# folder_separator = '/' # Unix folders
# better if you make sure that the target folder exists
if not os.path.exists(file_path):
# if it does not exist, you create it
os.makedirs(file_path)
# Create the target file (write mode).
outfile = open(file_path + '/' + file_name, 'w')
# loop over folder contents
for fname in glob.iglob("%s/*" % img_path):
# for every file found you take only the name (assuming that structure is folder/file.ext)
file_name_in_imgPath = fname.split('\\')[1]
# we want to avoid to write 'folders' in the target file
if os.path.isfile(file_name_in_imgPath):
# write filename in the target file
outfile.write(str(file_name_in_imgPath) + '\n')
outfile.close()

How to pass variables from Python to CMD in Windows

I am writing a Python (2.5) script that will search through a folder, grab a specific file, and then pass the file and a command to a CMD shell in Windows. This needs to be done with the command program.exe /rptcsv <file being created> <file being used>. Additionally, the .exe HAS to be executed from the C:\Program Files (x86)\Reporter folder, hence the working directory change.
Here's my problem: I need Python to search for one specific file (fnmatch), and then pass the entire command to a shell. When this is done correctly, the process runs in the background without launching the GUI.
I've seen the posts about stdin. stdout, etc, and I don't need anything piped back- I just need Python to pass the whole thing. Right now what happens is Python launches the GUI but fails to pass the command complete with variables. Is there a way to do this?
I'm still a Python n00b, so please forgive any obvious mistakes.
MC01 = 'WKST01*.bat'
MC02 = 'WKST02*.bat'
files = os.listdir(FOLDER)
MC01_CMD = fnmatch.filter(files, MC01)
MC01_CSV = "MC01.csv"
exe = ("reporter.exe /rptcsv", MC01_CSV, MC01_CMD)
os.chdir("C:\Program Files (x86)\Reporter")
os.system("exe")
Edit: Earlier in my code, I used os.walk in the FOLDER:
print "Walking directory..."
for root, dirs, files in os.walk(FOLDER):
for file in files:
pathname = os.path.join(root, file)
Because I switched working directories, it's searching for the MC01_CMD file in C:\Program Files (x86)\Reporter and (of course) it's not there. Is there a way to join pathname and MC01_CMD without creating a new variable so it's got the correct location of MC01_CMD?
os.system takes a single string as command. In your case, that is the string "exe". You need to concatenate the filenames returnd by fnmatch.filter, using " ".join(exe) and then call os.system(command). Note the missing " in os.system(command).
For finding the file in a tree, just concatenate the (absolute path of your) base folder of your os.walk call with the basedir and the filename. You can filter on the filenames during os.walk, too.
MC01 = 'WKST01*.bat'
MC02 = 'WKST02*.bat'
def collect_files(folder, pattern):
for basedir, dirs, files in os.walk(folder):
for file in fnmatch.filter(files, pattern):
yield os.path.join(folder, basedir, file)
MC01_CMD = collect_files(FOLDER, MC01)
MC01_CSV = "MC01.csv"
command = "reporter.exe /rptcsv "+ MC01_CSV + " " + " ".join(MC01_CMD)
os.chdir("C:\Program Files (x86)\Reporter")
os.system(command)
Variables aren't be expanded in string literals. So os.system("exe") is the same as typing exe in cmd and pressing enter.
So my guess for the correct code would be:
MC01_CSV = MC01 + ".csv"
os.chdir("C:\Program Files (x86)\Reporter")
os.system("reporter.exe /rptcsv " + MC01_CSV + " " + MC01_CMD)

Deleting files which start with a name Python

I have a few files I want to delete, they have the same name at the start but have different version numbers. Does anyone know how to delete files using the start of their name?
Eg.
version_1.1
version_1.2
Is there a way of delting any file that starts with the name version?
Thanks
import os, glob
for filename in glob.glob("mypath/version*"):
os.remove(filename)
Substitute the correct path (or . (= current directory)) for mypath. And make sure you don't get the path wrong :)
This will raise an Exception if a file is currently in use.
If you really want to use Python, you can just use a combination of os.listdir(), which returns a listing of all the files in a certain directory, and os.remove().
I.e.:
my_dir = # enter the dir name
for fname in os.listdir(my_dir):
if fname.startswith("version"):
os.remove(os.path.join(my_dir, fname))
However, as other answers pointed out, you really don't have to use Python for this, the shell probably natively supports such an operation.
In which language?
In bash (Linux / Unix) you could use:
rm version*
or in batch (Windows / DOS) you could use:
del version*
If you want to write something to do this in Python it would be fairly easy - just look at the documentation for regular expressions.
edit:
just for reference, this is how to do it in Perl:
opendir (folder, "./") || die ("Cannot open directory!");
#files = readdir (folder);
closedir (folder);
unlink foreach (grep /^version/, #files);
import os
os.chdir("/home/path")
for file in os.listdir("."):
if os.path.isfile(file) and file.startswith("version"):
try:
os.remove(file)
except Exception,e:
print e
The following function will remove all files and folders in a directory which start with a common string:
import os
import shutil
def cleanse_folder(directory, prefix):
for item in os.listdir(directory):
path = os.path.join(directory, item)
if item.startswith(prefix):
if os.path.isfile(path):
os.remove(path)
elif os.path.isdir(os.path.join(directory, item)):
shutil.rmtree(path)
else:
print("A simlink or something called {} was not deleted.".format(item))
import os
import re
directory = "./uploaded"
pattern = "1638813371180"
files_in_directory = os.listdir(directory)
filtered_files = [file for file in files_in_directory if ( re.search(pattern,file))]
for file in filtered_files:
path_to_file = os.path.join(directory, file)
os.remove(path_to_file)

Categories