» Go创建命令行程序grep » 2. 开发 » 2.7 支持管道

支持管道

在类 Unix 操作系统(如 Linux 和 macOS)中,管道是一种用于进程间通信(IPC)的机制,允许一个进程的输出被用作另一个进程的输入。

在 Shell 命令中,管道由 | 符号表示。当你使用类似 command1 | command2 的命令时,意味着 command1 的标准输出被连接到 command2 的标准输入。

例如:

cat file.txt | grep "pattern"

在此命令中,file.txt 的内容被作为输入传递到 grep 命令,该命令搜索指定的模式。

修改 main.go 以使file_path 参数可选:

@@ -12,7 +12,7 @@ import (
 func main() {
        // Set custom usage message
        flag.Usage = func() {
-               fmt.Fprintf(os.Stderr, "Usage: %s [options] pattern file_path\n", os.Args[0])
+               fmt.Fprintf(os.Stderr, "Usage: %s [options] pattern [file_path]\n", os.Args[0])
                fmt.Println("Options:")
                flag.PrintDefaults()
        }
@@ -30,12 +30,15 @@ func main() {
        // pattern - The pattern to search for
        // file_path - The path to the file to search in
        args := flag.Args()
-       if len(args) < 2 {
-               fmt.Println("Both pattern and file_path are required.")
+       if len(args) < 1 {
+               fmt.Println("Argument `pattern` is required.")
                flag.Usage()
                os.Exit(0)
        }
-       pattern, filePath := args[0], args[1]
+       pattern, filePath := args[0], ""
+       if len(args) > 1 {
+               filePath = args[1]
+       }
 
        options := &grep.Options{}
        if *ignoreCaseFlag {

文件路径为空时无法做递归搜索,故修改 main.go:

@@ -48,7 +51,7 @@ func main() {
        var result grep.MatchResult
        var err error
 
-       if *recursiveFlag {
+       if *recursiveFlag && filePath != "" {
                result, err = grep.GrepRecursive(pattern, filePath, options)
                if err != nil {
                        log.Fatal("Failed to do recursive grep, error:", err)

pkg/grep/search.go 中,当 filePath 为空时,从标准输入读取内容:

func readFileLines(filePath string) ([]string, error) {
	var scanner *bufio.Scanner
	if filePath == "" { // 从stdin读取,通常是管道
		scanner = bufio.NewScanner(os.Stdin)
	} else { // 从文件读取
		file, err := os.Open(filePath)
		if err != nil {
			return nil, err
		}
		defer file.Close()

		// 创建一个扫描器进行按行读取
		scanner = bufio.NewScanner(file)
	}

	var lines []string
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}
	return lines, nil
}

现在可以执行以下操作:

cat main.go | go run main.go -n result

其结果:

51: var result grep.MatchResult
55: result, err = grep.GrepRecursive(pattern, filePath, options)
60: result, err = grep.Grep(pattern, filePath, options)
67: fmt.Println(grep.GrepCount(result))
69: printResult(result, *lineNumberFlag)
73: func printResult(result grep.MatchResult, lineNumberOption bool) {
75: fileCount := len(result)
77: for filePath, items := range result {

重新安装后,你的 gorep 也可以支持管道。

cat main.go | gorep -n result

其结果与上面相同。