0

I am trying to write a python script to save me time copy/pasting updated files.

This is what i got so far:

import os
import shutil

src_files = [
    'C:/orig/path/to/some/file/module_some_file.php',
    'C:/orig/path/to/some/other/file/module_some_file.php',
    'C:/orig/path/to/some/third/file/module_with_diff_name_after_module.php',
    # ....
]

dest_files = [
    'C:/copy/path/to/some/file/module_some_file.php',
    'C:/copy/path/to/some/other/file/module_some_file.php',
    'C:/orig/path/to/some/third/file/module_with_diff_name_after_module.php',
    # ....
]

for i in range(len(src_files)): 
  FILE_DIR=os.path.dirname(dest_files[i])
  if not os.path.exists(FILE_DIR):
    os.makedirs(FILE_DIR)
  if (os.path.isfile(src_files[i])):
    shutil.copy(src_files[i], dest_files[i])

This way works but i would like to achieve that i enter for example py copy.py module --src "C:/orig/path/to/some" --dest "C:/copy/path/to/some" and the script will search all files where name contains "module". A wildcard kind of solution. My question(s), can i achieve what i want with python? And can someone help me with substituting the lists for some elegant wildcard/search solution?

Environment: Windows / python 3.6.1

This is literally my first python script so please have some mercy :)

2
  • 1
    Note: Don't name your python script the same as the builtin modules, if you try to use the copy module it will mask it. Commented Apr 11, 2017 at 0:50
  • Got it. Thanks for the hint! Commented Apr 11, 2017 at 2:25

3 Answers 3

3

It looks like you're doing great so far, but the two pieces you're missing are (1) how to parse arguments from the command line, and (2) how to use wildcards in Python. I'll address them in that order:

Command line arguments

Python's sys module is an easy way to get started with command line arguments.

Suppose I have a file called test_args.py:

# test_args.py
import sys

print(sys.argv)

If I typed python test_argv.py arg1 arg2 arg3 at my command line, it would print out a list of the arguments used:

# ['test_args.py', 'arg1', 'arg2', 'arg3']

For more advanced arg parsing, see the argparse docs: https://docs.python.org/3.3/library/argparse.html

Wildcards

Python's built-in glob module should be able to achieve what you're looking for. It's pretty simple. If my directory is called "myfolder" and has this structure:

myfolder
.
├── data1.csv
├── data2.csv
├── file1.txt
├── file2.txt
└── file3.txt

Here's what Python provides me:

import glob

pattern1 = '*.txt'
matching_filenames_1 = glob.glob(pattern1)
print(matching_filenames_1)
# ['file1.txt', 'file2.txt', 'file3.txt']

pattern2 = '*2*'
matching_filenames_2 = glob.glob(pattern2)
print(matching_filenames_2)
# ['data2.csv', 'file2.txt']

Argparse

As I said above, the argparse module gives you a lot of functionality out of the box. Check it out:

import argparse
import shutil
import sys

def parse_args():
    # Set up the parser, and tell it what arguments to expect
    parser = argparse.ArgumentParser()
    parser.add_argument('--src', nargs='*')
    parser.add_argument('--dest')
    # Get the list of arguments from the command line, starting from
    # index 1 (since index 0 is the name of this file).
    raw_args = sys.argv[1:]
    # Parse the args, and return the object containing them
    args = parser.parse_args(raw_args)
    return args

def copy_files():
    # Use our custom function to parse the arguments
    args = parse_args()
    source_files = args.src
    destination_folder = args.dest
    # Copy each file into the destination folder
    for file in source_files:
        shutil.copy(file, destination_folder)

copy_files()
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you very much Carolyn. That was very helpful! i have rewritten the script following your suggestion and it works like a charm. I have added the snippet as an additional answer. I am not sure yet which answer to accept since abccd's answer is more what i was looking for, your help lead me to my solution and my solution fits perfect to my question...
If both worked for you, it's fine to accept either one :)
Or just accept your own, which is perfectly fine :)
1

You can do so using glob to search all files with a certain name. Here's an example:

import shutil
import glob, sys, os

src_files = glob.glob1(sys.argv[3], '*%s*'%sys.argv[1])
dest = sys.argv[5]
for name in src_files:
    shutil.copy(os.path.join(sys.argv[3], name), os.path.join(dest, name))

py modulename.py module --src "C:/orig/path/to/some" --dest "C:/copy/path/to/some"

To improve this code, you can use argparse instead of sys.argv.

Here's a more professional way:

import shutil, argparse
import glob, os

parser = argparse.ArgumentParser()
parser.add_argument('name', metavar='N', type=str, nargs='+')
parser.add_argument('--src', required=False)
parser.add_argument('--dest',  required=False)
parsed = parser.parse_args()

src_files = glob.glob1(parsed.src, '*%s*'%parsed.name[0])
dest = parsed.dest
for name in src_files:
    shutil.copy(os.path.join(parsed.src, name), os.path.join(dest, name))

Like all other command calling, you will need to use the = when calling: py modulename.py module --src="C:/orig/path/to/some" --dest="C:/copy/path/to/some"

3 Comments

Thank you too abccd. I have to adapt my solution (posted as answer) to your bottom snippet. It seems that what i have done there is not optimal or strange. Still got to understand why os.path.join(args.dest, filename) would not give me the dest but src path when using glob.iglob(args.src+'/**/*'+args.search+'*', recursive=True).
I don't believe that would happen:( perhaps there's a mistake something else?
Maybe you should print to see what filename is, perhaps that will give you a hint.
0

Based on the comments and answers, i have rewritten the script to following:

import argparse, shutil, glob, sys, ntpath, os

def parse_args():
    # Set up the parser, and tell it what arguments to expect
    parser = argparse.ArgumentParser()
    parser.add_argument('--search')
    parser.add_argument('--src')
    parser.add_argument('--dest')
    # Get the list of arguments from the command line, starting from
    # index 1 (since index 0 is the name of this file).
    raw_args = sys.argv[1:]
    # Parse the args, and return the object containing them
    args = parser.parse_args(raw_args)
    return args

args = parse_args()

for filename in glob.iglob(args.src+'/**/*'+args.search+'*', recursive=True):
    src_file = os.path.join(args.src, filename)
    dest_file = filename.replace(args.src,args.dest)
    dest_dir = os.path.dirname(dest_file)
    if os.path.isdir(src_file):
        continue
    if not os.path.exists(dest_dir):
        os.makedirs(dest_dir)
    if (os.path.isfile(src_file)):
        shutil.copy(src_file, dest_file)

Now i can do this:

py copy_files.py --search "searchterm" --src "C:/orig/path/to/some" --dest "C:/copy/path/to/some"

After the edit of abccd i suppose i can still improve these lines:

for filename in glob.iglob(args.src+'/**/*'+args.search+'*', recursive=True):
    src_file = os.path.join(args.src, filename)
    dest_file = filename.replace(args.src,args.dest)
    dest_dir = os.path.dirname(dest_file)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.