I often find myself wanting to copy directories around with either:
- having to copy only specific extensions, or
- having to exclude specific extensions.
For this task, I wrote some Python which I am calling from the cmd
.
The Python script is humbly called copy_master.py
and I have also set an environment variable for it called %Pcopy%
.
copy_master.py
def classify(dirs):
return {(1, 0): dirs, (0, 1): dirs[::-1], (0, 0): [None, None], (1, 1): [1, 1]}[tuple(map(os.path.isdir, dirs))]
def include(d, files):
return [f for f in files if not any(f.endswith('.' + x) for x in sys.argv[4].split(',')) and not os.path.isdir(os.path.join(d, f))]
def refuse(d, files):
return [f for f in files if any(f.endswith('.' + x) for x in sys.argv[4].split(',')) and not os.path.isdir(os.path.join(d, f))]
if __name__ == '__main__':
import shutil
import os
import sys
if len(sys.argv) < 3 or sys.argv[1].lower() == 'help':
print('\nParameters:: \n\t > Source and Destination directories: order does not matter; one of the specified '
'directories MUST NOT exist and this is taken to be the destination. \n\t > Mode: (+) for copying specific'
' file extensions only OR (-) for skipping specific extensions. \n\t > Extensions: comma delimited list of'
' file extensions to be copied or ignored.\n\nExample Usage:\n\t{0:} {1:} + bdf -> Copies only bdf files '
'from {1:} to {0:}\n\t{0:} {1:} - f06,xdb -> Copies everything but f06 & xbd files from {1:} to {0:}'.format(r'I:\do\not\exist', r'I:\have\the\files\you\want'))
elif len(sys.argv) > 2:
src, dst = classify(sys.argv[1:3])
if src is None:
raise IOError('The source directory should exist!')
elif src == 1:
raise IOError('The destination directory should not exist!')
if len(sys.argv) == 5:
mode = sys.argv[3]
print('\rCopying...', end='')
shutil.copytree(src, dst, ignore={'+': include, '-': refuse}[mode])
print('\rCopying completed')
else:
q = input('Extensions and execution mode have not been specified. Everything is going to be copied! Proceed (Y/N)?\n::')
if q.lower() in ['n', 'no']:
exit()
else:
print('Copying...')
shutil.copytree(src, dst)
Example usages (with the command prompt open, type):
copy only specific extension:
%Pcopy% I:\do\not\exist I:\have\the\files\you\want + txt # ^
exclude specific extensions from the copying:
%Pcopy% I:\do\not\exist I:\have\the\files\you\want - xlsx,bat # ^
There are a couple of things I am not so happy\sure about:
- Have I sacrificed too much readability in the
classify()
function? Aredict
s meant to be (ab)used like that? What should I be returning in the(1, 1)
case (if both directories exist)? - Is the use of global
sys.argv
ininclude()
andrefuse()
ok? - How can I handle the different ways a user might use the script in the best possible way? Not quite happy about the
if
statements in the__main__
.
Apart from the points discussed above, feel free to suggest other changes or even an entirely different approach. And by all means, use the script if you like it ;)
Bonus Points:
Currently, directories that have to be quoted to be recognized by the cmd
are not handled (e.g., C:\users\Example\This&That
, note the &
)
glob
module with the "glob" patterns to match files and directories? \$\endgroup\$include()
andrefuse()
. Will look into it. \$\endgroup\$