» Make grep CLI App in Python » 2. Development » 2.9 Support Multiple File Paths

Support Multiple File Paths

parser.add_argument('file_paths', nargs='*', default=[], help='File paths to search in')

nargs='*' allows the script to accept zero or more file paths.

Then you can do this:

grepy pattern file1.txt file2.txt file3.txt
# Or
grepy pattern *.txt

Note:
Using *.txt as an argument in the command line directly may not work as expected because the shell (e.g., Bash) is responsible for expanding wildcard patterns like *.txt. The argparse module in Python won't automatically perform this expansion.
However, you can use the glob module to manually expand the wildcard pattern.

Add multiple paths version grep in grepy/grep.py:

def grep_m(pattern: Union[str, re.Pattern],
         file_paths: Union[List[str], Literal['']], options: List[str]):
    "grep multiple files"
    if not file_paths:
        return grep(pattern, "", options)
    result = {}
    for file_path in file_paths:
        result.update(grep(pattern, file_path, options))
    return result


def grep_recursive_m(pattern: Union[str, re.Pattern],
         file_paths: Union[List[str], Literal['']], options: List[str]):
    "grep multiple dirs recursively"
    result = {}
    for file_path in file_paths:
        result.update(grep_recursive(pattern, file_path, options))
    return result

Modify the argparse part in grepy/cli.py:

@@ -1,16 +1,16 @@
 import argparse
 from typing import List, Dict
 
-from grepy.grep import grep, grep_recursive, grep_count, MatchResults
+from grepy.grep import grep_recursive_m, grep_count, grep_m, MatchResults
 
 
 def main():
     parser = argparse.ArgumentParser(description='''
             A grep-like command-line utility from LiteRank,
             see https://literank.com/project/9/intro''')
     parser.add_argument('pattern', type=str, help='The pattern to search for')
-    parser.add_argument('file_path', type=str, nargs="?", default="",
-                        help='The path to the file to search in')
+    parser.add_argument('file_paths', nargs="*", default=[],
+                        help='File paths to search in')
 
     # Optional arguments
     parser.add_argument('-c', '--count', action='store_true',
@@ -31,11 +31,11 @@ def main():
 
     args = parser.parse_args()
 
-    if args.recursive and args.file_path != "":
-        result = grep_recursive(args.pattern,
-                                args.file_path, get_options(args))
+    if args.recursive and args.file_paths != "":
+        result = grep_recursive_m(args.pattern,
+                                  args.file_paths, get_options(args))
     else:
-        result = grep(args.pattern, args.file_path, get_options(args))
+        result = grep_m(args.pattern, args.file_paths, get_options(args))
 
     if args.count:
         print(grep_count(result))

Then you can do this:

grepy -n result grepy/*py

# Or
grepy -n result grepy/cli.py grepy/grep.py

Result:

grepy/cli.py:
35: result = grep_recursive_m(args.pattern,
38: result = grep_m(args.pattern, args.file_paths, get_options(args))
41: print(grep_count(result))
43: print_result(result, args.line_number)
55: def print_result(result: Dict[str, MatchResults], line_number_option: bool):
57: file_count = len(result)
58: for file_path, lines in result.items():

grepy/grep.py:
21: result = {}
23: result.update(grep(pattern, file_path, options))
24: return result
51: def grep_count(result: Dict[str, MatchResults]):
52: return sum([len(v) for v in result.values()])
58: result = {}
60: result.update(grep_recursive(pattern, file_path, options))
61: return result
66: results = {}
70: results.update(grep(pattern, file_path, options))
71: return results

Nice👍🏻! So far so good!

PrevNext