» Make grep CLI App in Python » 2. Development » 2.8 Support Pipes

Support Pipes

In Unix-like operating systems (such as Linux and macOS), a pipe is a mechanism for interprocess communication (IPC) that allows the output of one process to be used as the input of another.

Pipes are represented by the | symbol in shell commands. When you use a command like command1 | command2, it means that the standard output of command1 is connected to the standard input of command2.

For example:

cat file.txt | grep "pattern"

In this command, the content of file.txt is piped as input to the grep command, which searches for the specified pattern.

In Windows, a similar mechanism exists, but it is called a pipeline.

For example:

Get-Content file.txt | Select-String "pattern"

In this PowerShell command, Get-Content retrieves the content of file.txt, and the output is piped to Select-String for pattern matching.

Modify grepy/cli.py to make file_path argument optional:

     parser.add_argument('pattern', type=str, help='The pattern to search for')
-    parser.add_argument('file_path', type=str,
+    parser.add_argument('file_path', type=str, nargs="?", default="",
                         help='The path to the file to search in')

You can add an optional positional argument by using the nargs parameter with a value of '?'. This makes the argument optional, allowing it to be provided or omitted on the command line.

You cannot do a recursive search when there's no file paths, modify grepy/cli.py:

-    if args.recursive:
+    if args.recursive and args.file_path != "":
         result = grep_recursive(args.pattern,
                                 args.file_path, get_options(args))

Read the content from standard input when file_path is empty in grepy/grep.py:

def grep(pattern: Union[str, re.Pattern],
         file_path: str, options: List[str] = []):
    if not file_path:  # read from stdin, usually the pipe
        lines = sys.stdin.read().split('\n')
    else:  # read from files
        with open(file_path, 'r') as file:
                lines = file.readlines()
            except UnicodeDecodeError:  # filter out binary files
                return {file_path: []}

    if options:
        if 'i' in options:
            pattern = re.compile(pattern, re.IGNORECASE)
        if 'v' in options:
            matching_lines = _filter_lines(pattern, lines, False)
            matching_lines = _filter_lines(pattern, lines, True)
        matching_lines = _filter_lines(pattern, lines, True)

    return {file_path: matching_lines}

Now you can do this:

cat grepy/cli.py | python3 -m grepy.cli -n result

Its result:

35: result = grep_recursive(args.pattern,
38: result = grep(args.pattern, args.file_path, 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():

After re-installation, your grepy supports pipes as well.

cat grepy/cli.py | grepy -n result

It produces the same result as above.