1

I'm new to python and currently experimenting using argparse to add command line options. However my code is not working, despite looking at various online tutorials and reading up on argparse I still don't fully understand it. My problem is whenever I try to call my -option it gives me a find.py error: argument regex:

Here is my call:

./find.py ../Python -name '[0-9]*\.txt'

../Python is one directory behind my current one and has a list of files/directories. Without the -name option I print out the files with their path (this works fine) but with the -name option I want to print out files matching the regex but it won't work. Here is what I currently have:

#!/usr/bin/python2.7 import os, sys, argparse,re from stat import * def regex_type(s, pattern=re.compile(r"[a-f0-9A-F]")): if not pattern.match(s): raise argparse.ArgumentTypeError return s def main(): direc = sys.argv[1] for f in os.listdir(direc): pathname = os.path.join(direc, f) mode = os.stat(pathname).st_mode if S_ISREG(mode): print pathname parser = argparse.ArgumentParser() parser.add_argument( '-name', default=[sys.stdin], nargs="*") parser.add_argument('regex', type=regex_type) args = parser.parse_args() if __name__ == '__main__': main() 

1 Answer 1

1

I tweaked your type function to be more informative:

def regex_type(s, pattern=re.compile(r"[a-f0-9A-F]")): print('regex string', s) if not pattern.match(s): raise argparse.ArgumentTypeError('pattern not match') return s 

Called with

2104:~/mypy$ python2 stack50072557.py . 

I get:

<director list> ('regex string', '.') usage: stack50072557.py [-h] [-name [NAME [NAME ...]]] regex stack50072557.py: error: argument regex: pattern not match 

So it tries to pass sys.argv[1], the first string after the script name, to the regex_type function. If it fails it issues the error message and usage.

OK, the problem was the ..; I'll make a directory:

2108:~/mypy$ mkdir foo 2136:~/mypy$ python2 stack50072557.py foo ('regex string', 'foo') Namespace(name=[<open file '<stdin>', mode 'r' at 0x7f3bea2370c0>], regex='foo') 2138:~/mypy$ python2 stack50072557.py foo -name a b c ('regex string', 'foo') Namespace(name=['a', 'b', 'c'], regex='foo') 

The strings following '-name' are allocated to that attribute. There's nothing in your code that will test them or pass them through the regex_type function. Only the first non-flag string does that.

Reading sys.argv[1] initially does not remove it from the list. It's still there for use by the parser.

I would set up a parser that uses a store_true --name argument, and 2 positionals - one for the dir and the other for regex.

After parsing check args.name. If false print the contents of args.dir. If true, perform your args.regex filter on those contents. glob might be useful.

The parser finds out what your user wants. Your own code acts on it. Especially as a beginner, it is easier and cleaner to separate the two steps.


With:

def parse(argv=None): parser = argparse.ArgumentParser() parser.add_argument('-n', '--name', action='store_true') parser.add_argument('--dir', default='.') parser.add_argument('--regex', default=r"[a-f0-9A-F]") args = parser.parse_args(argv) print(args) return args def main(argv=None): args = parse(argv) dirls = os.listdir(args.dir) if args.name: dirls = [f for f in dirls if re.match(args.regex, f)] print(dirls) else: print(dirls) 

I get runs like:

1005:~/mypy$ python stack50072557.py Namespace(dir='.', name=False, regex='[a-f0-9A-F]') ['test.npz', 'stack49909128.txt', 'stack49969840.txt', 'stack49824248.py', 'test.h5', 'stack50072557.py', 'stack49963862.npy', 'Mcoo.npz', 'test_attribute.h5', 'stack49969861.py', 'stack49969605.py', 'stack49454474.py', 'Mcsr.npz', 'Mdense.npy', 'stack49859957.txt', 'stack49408644.py', 'Mdok', 'test.mat5', 'stack50012754.py', 'foo', 'test'] 1007:~/mypy$ python stack50072557.py -n Namespace(dir='.', name=True, regex='[a-f0-9A-F]') ['foo'] 1007:~/mypy$ python stack50072557.py -n --regex='.*\.txt' Namespace(dir='.', name=True, regex='.*\\.txt') ['stack49909128.txt', 'stack49969840.txt', 'stack49859957.txt'] 

and help:

1007:~/mypy$ python stack50072557.py -h usage: stack50072557.py [-h] [-n] [--dir DIR] [--regex REGEX] optional arguments: -h, --help show this help message and exit -n, --name --dir DIR --regex REGEX 

If I change the dir line to:

parser.add_argument('dir', default='.') 

help is now

1553:~/mypy$ python stack50072557.py -h usage: stack50072557.py [-h] [-n] [--regex REGEX] dir positional arguments: dir optional arguments: -h, --help show this help message and exit -n, --name --regex REGEX 

and runs are:

1704:~/mypy$ python stack50072557.py -n usage: stack50072557.py [-h] [-n] [--regex REGEX] dir stack50072557.py: error: too few arguments 1705:~/mypy$ python stack50072557.py . -n Namespace(dir='.', name=True, regex='[a-f0-9A-F]') ['foo'] 1705:~/mypy$ python stack50072557.py ../mypy -n --regex='.*\.txt' Namespace(dir='../mypy', name=True, regex='.*\\.txt') ['stack49909128.txt', 'stack49969840.txt', 'stack49859957.txt'] 

I get the error because it now requires a directory, even it is '.'.

Note that the script still uses:

if __name__ == '__main__': main() 

My main loads the dir, and applies the regex filter to that list of names. My args.dir replaces your direc.

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

4 Comments

What if I dont need to use argv=None as an argument to the function? I just need to take a pathname from the command line, go to that directory, and analyze the files inside. How would I go about that without using sys.argv[1]?
Does just having the argparse module take care of parsing arguments and options for me? Or do I need to use sys.argv[1] to get user input? I really appreciate the help you've been giving me
argv=None means the function argument is optional. You can omit it. My code accepts the directory as '--dir <dirname>'. The os.listdir(args.dir) is an abbreviated version of your directory load.
I still can't figure out how to match the files with the given regex if -name is called. It works when I call it like ./find.py ../directory but when I do ./find.py ../directory -name [a-z] its telling me ./find.py: No match.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.