From 0d21e17d88823b319e445f44e0d940fd4ef28683 Mon Sep 17 00:00:00 2001 From: mstxq17 <154380808@qq.com> Date: Mon, 30 Oct 2023 13:45:29 +0800 Subject: [PATCH] feat: support grep & grep -v feature --- cmd/cmd.go | 50 ++++++++++++++++++++++++++++++++++---- cmd/cobra.go | 15 ++++++++++++ cmd/root.go | 64 +++++++++++++++++++++++++++++-------------------- core/matcher.go | 16 +++++++++++++ 4 files changed, 115 insertions(+), 30 deletions(-) create mode 100644 cmd/cobra.go diff --git a/cmd/cmd.go b/cmd/cmd.go index a855191..4a4be29 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1,16 +1,16 @@ package cmd import ( + "bufio" "fmt" + "github.com/mstxq17/MoreFind/core" "github.com/mstxq17/MoreFind/update" "github.com/mstxq17/MoreFind/vars" "github.com/spf13/cobra" + "log" + "strings" ) -func init() { - rootCmd.AddCommand(versionCmd) -} - var versionCmd = &cobra.Command{ Use: "version", Short: "Print the semantic version number of MoreFind", @@ -26,3 +26,45 @@ var versionCmd = &cobra.Command{ fmt.Println("") }, } + +var pattern string +var inverseMatch bool // Define a variable to hold the value of the inverse match flag + +var grepCmd = &cobra.Command{ + Use: "grep", + Short: "If no grep , use this", + Long: `The grep command filters and displays lines matching a given pattern within files, akin to the Unix 'grep' command but without the second option.`, + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 1 { + pattern = args[0] + } + fileStdin, _ := handleStdin(file) + defer func() { + if err := fileStdin.Close(); err != nil { + log.Fatal(err) + } + }() + reader := bufio.NewReader(fileStdin) + scanner := bufio.NewScanner(reader) + buf := make([]byte, 0, 64*1024) + scanner.Buffer(buf, MaxTokenSize) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + matchLine, err := core.MatchLine(line, pattern, inverseMatch) + if err == nil && matchLine != "" { + fmt.Println(matchLine) + } + } + }, +} + +func init() { + // try other style to parse params + // 尝试使用不同的风格命令参数获取 + grepCmd.Flags().StringVarP(&pattern, "pattern", "P", "", "Pattern to search") + grepCmd.Flags().BoolVarP(&inverseMatch, "inverse-match", "v", false, "Invert the match") + grepCmd.SetUsageTemplate(usageTemplate) + grepCmd.SetHelpTemplate(helpTemplate) + rootCmd.AddCommand(versionCmd) + rootCmd.AddCommand(grepCmd) +} diff --git a/cmd/cobra.go b/cmd/cobra.go new file mode 100644 index 0000000..42ef936 --- /dev/null +++ b/cmd/cobra.go @@ -0,0 +1,15 @@ +package cmd + +// Help template defines the format of the help message. +var helpTemplate = `{{.Long | trim}} + +Usage: + {{.CommandPath}} [flags] +{{if .Runnable}} +Flags: +{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}} + +` + +// Usage template defines the format of the usage message. +var usageTemplate = `Usage: {{.CommandPath}} [flags]` diff --git a/cmd/root.go b/cmd/root.go index 5aa4d06..dfe59d9 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -19,6 +19,10 @@ import ( "strings" ) +const ( + MaxTokenSize = 512 * 1024 * 1024 +) + var logger *log.Logger var NewLine string @@ -240,6 +244,29 @@ func genIP(cidr string) { } } +func handleStdin(file string) (*os.File, os.FileInfo) { + var _file *os.File + if file != "" { + var err error + _file, err = os.Open(file) + if err != nil { + panic(err) + } + } else { + _file = os.Stdin + } + // use features to solve whether has input + // 利用特性解决程序是否有输入的问题 + fi, _ := _file.Stat() + if (fi.Mode() & os.ModeCharDevice) != 0 { + logger.Println("No input found, exit ...") + // optimize exit logic + // 优化退出逻辑 + os.Exit(0) + } + return _file, fi +} + func updateCommand(cmd *cobra.Command, args []string) { callBackError := func() *log.Logger { return logger @@ -260,32 +287,16 @@ func preCommand(cmd *cobra.Command, args []string) bool { } func runCommand(cmd *cobra.Command, args []string) { - var _file *os.File - if file != "" { - var err error - _file, err = os.Open(file) - if err != nil { - panic(err) + // unified data stream + // 统一数据流 + _file, fi := handleStdin(file) + // prevent memory leaking + // 防止内存泄漏 + defer func() { + if err := _file.Close(); err != nil { + log.Fatal(err) } - // prevent memory leaking - // 防止内存泄漏 - defer func() { - if err = _file.Close(); err != nil { - log.Fatal(err) - } - }() - } else { - _file = os.Stdin - } - // use features to solve whether has input - // 利用特性解决程序是否有输入的问题 - fi, _ := _file.Stat() - if (fi.Mode() & os.ModeCharDevice) != 0 { - logger.Println("No input found, exit ...") - // optimize exit logic - // 优化退出逻辑 - os.Exit(0) - } + }() // define global reader of input // 定义全局输入读取流 var scanner *bufio.Scanner @@ -303,7 +314,7 @@ func runCommand(cmd *cobra.Command, args []string) { buf := make([]byte, 0, 64*1024) // support maximum 512MB buffer every line // 支持最大读取单行 512MB 大小 - scanner.Buffer(buf, 512*1024*1024) + scanner.Buffer(buf, MaxTokenSize) // todo: current structure may be chaotic, should abstract the handle process if myCidr == "__pipe__" { for scanner.Scan() { @@ -548,6 +559,7 @@ func init() { // help me a lot, so log it in the code, google dork: "flag needs an argument: cobra" // 感谢 https://stackoverflow.com/questions/70182858/how-to-create-flag-with-or-without-argument-in-golang-using-cobra 提供了如何解决--filter 默认参数的问题 rootCmd.PersistentFlags().Lookup("filter").NoOptDefVal = "js,css,json,png,jpg,html,xml,zip,rar" + rootCmd.PersistentFlags().StringVarP(&myCidr, "cidr", "c", "", vars.CidrHelpEn) rootCmd.PersistentFlags().Lookup("cidr").NoOptDefVal = "__pipe__" rootCmd.PersistentFlags().StringVarP(&myLimitLen, "len", "l", "", vars.LimitLenHelpEn) diff --git a/core/matcher.go b/core/matcher.go index 9a8bc95..19887e6 100644 --- a/core/matcher.go +++ b/core/matcher.go @@ -1 +1,17 @@ package core + +import "regexp" + +func MatchLine(line, pattern string, inverse bool) (string, error) { + regexPattern, err := regexp.Compile(pattern) + if err != nil { + return "", err + } + if regexPattern.MatchString(line) && !inverse { + return line, nil + } + if !regexPattern.MatchString(line) && inverse { + return line, nil + } + return "", nil +}