diff --git a/cmd/root.go b/cmd/root.go index ddd0d31..c8db5ec 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -18,33 +18,31 @@ package cmd import ( "bytes" - "github.com/tlinden/tablizer/lib" "fmt" "github.com/spf13/cobra" + "github.com/tlinden/tablizer/lib" "log" "os" "os/exec" ) -var helpCmd = &cobra.Command{ - Use: "help", - Short: "Show documentation", - Run: func(cmd *cobra.Command, args []string) { - man := exec.Command("less", "-") +var ShowManual = false - var b bytes.Buffer - b.Write([]byte(manpage)) +func man() { + man := exec.Command("less", "-") - man.Stdout = os.Stdout - man.Stdin = &b - man.Stderr = os.Stderr + var b bytes.Buffer + b.Write([]byte(manpage)) - err := man.Run() + man.Stdout = os.Stdout + man.Stdin = &b + man.Stderr = os.Stderr - if err != nil { - log.Fatal(err) - } - }, + err := man.Run() + + if err != nil { + log.Fatal(err) + } } var rootCmd = &cobra.Command{ @@ -57,6 +55,11 @@ var rootCmd = &cobra.Command{ return nil } + if ShowManual { + man() + return nil + } + err := lib.PrepareColumns() if err != nil { return err @@ -81,7 +84,9 @@ func Execute() { func init() { rootCmd.PersistentFlags().BoolVarP(&lib.Debug, "debug", "d", false, "Enable debugging") rootCmd.PersistentFlags().BoolVarP(&lib.NoNumbering, "no-numbering", "n", false, "Disable header numbering") - rootCmd.PersistentFlags().BoolVarP(&lib.ShowVersion, "version", "v", false, "Print program version") + rootCmd.PersistentFlags().BoolVarP(&lib.ShowVersion, "version", "V", false, "Print program version") + rootCmd.PersistentFlags().BoolVarP(&lib.InvertMatch, "invert-match", "v", false, "select non-matching rows") + rootCmd.PersistentFlags().BoolVarP(&ShowManual, "man", "m", false, "Display manual page") rootCmd.PersistentFlags().StringVarP(&lib.Separator, "separator", "s", "", "Custom field separator") rootCmd.PersistentFlags().StringVarP(&lib.Columns, "columns", "c", "", "Only show the speficied columns (separated by ,)") @@ -98,11 +103,4 @@ func init() { // same thing but more common, takes precedence over above group rootCmd.PersistentFlags().StringVarP(&lib.OutputMode, "output", "o", "", "Output mode - one of: orgtbl, markdown, extended, shell, ascii(default)") - - rootCmd.AddCommand(helpCmd) - - rootCmd.SetHelpCommand(&cobra.Command{ - Use: "no-help", - Hidden: true, - }) } diff --git a/cmd/tablizer.go b/cmd/tablizer.go index f7e26ad..b5d091e 100644 --- a/cmd/tablizer.go +++ b/cmd/tablizer.go @@ -11,6 +11,8 @@ SYNOPSIS -c, --columns string Only show the speficied columns (separated by ,) -d, --debug Enable debugging -h, --help help for tablizer + -v, --invert-match select non-matching rows + -m, --man Display manual page -n, --no-numbering Disable header numbering -o, --output string Output mode - one of: orgtbl, markdown, extended, ascii(default) -X, --extended Enable extended output @@ -38,7 +40,8 @@ DESCRIPTION Without any options it reads its input from "STDIN", but you can also specify a file as a parameter. If you want to reduce the output by some - regular expression, just specify it as its first parameters. Hence: + regular expression, just specify it as its first parameter. You may also + use the -v option to exclude all rows which match the pattern. Hence: # read from STDIN kubectl get pods | tablizer diff --git a/lib/common.go b/lib/common.go index dcda314..78b9c6c 100644 --- a/lib/common.go +++ b/lib/common.go @@ -31,12 +31,13 @@ var ( OutflagOrgtable bool OutflagShell bool OutputMode string + InvertMatch bool // used for validation validOutputmodes = "(orgtbl|markdown|extended|ascii)" // main program version - Version = "v1.0.4" + Version = "v1.0.5" // generated version string, used by -v contains lib.Version on // main branch, and lib.Version-$branch-$lastcommit-$date on diff --git a/lib/parser.go b/lib/parser.go index 53c0af6..ae7f50f 100644 --- a/lib/parser.go +++ b/lib/parser.go @@ -114,7 +114,11 @@ func parseFile(input io.Reader, pattern string) (Tabdata, error) { } else { // data processing if len(pattern) > 0 { - if !patternR.MatchString(line) { + if patternR.MatchString(line) == InvertMatch { + // by default -v is false, so if a line does NOT + // match the pattern, we will ignore it. However, + // if the user specified -v, the matching is inverted, + // so we ignore all lines, which DO match. continue } } @@ -151,7 +155,7 @@ func parseFile(input io.Reader, pattern string) (Tabdata, error) { } if scanner.Err() != nil { - return data, errors.Unwrap(fmt.Errorf("Regexp pattern %s is invalid: %w", pattern, scanner.Err())) + return data, errors.Unwrap(fmt.Errorf("Failed to read from io.Reader: %w", scanner.Err())) } if Debug { diff --git a/lib/parser_test.go b/lib/parser_test.go index 24fca81..396f6a3 100644 --- a/lib/parser_test.go +++ b/lib/parser_test.go @@ -18,6 +18,7 @@ along with this program. If not, see . package lib import ( + "fmt" "reflect" "strings" "testing" @@ -80,3 +81,57 @@ asd igig cxxxncnc t.Errorf("Parser returned invalid data\nExp: %+v\nGot: %+v\n", data, gotdata) } } + +func TestParserPatternmatching(t *testing.T) { + var tests = []struct { + entries [][]string + pattern string + invert bool + }{ + { + entries: [][]string{ + []string{ + "asd", + "igig", + "cxxxncnc", + }, + }, + pattern: "ig", + invert: false, + }, + { + entries: [][]string{ + []string{ + "19191", + "EDD 1", + "X", + }, + }, + pattern: "ig", + invert: true, + }, + } + + table := `ONE TWO THREE +asd igig cxxxncnc +19191 EDD 1 X` + + for _, tt := range tests { + testname := fmt.Sprintf("parse-with-inverted-pattern-%t", tt.invert) + t.Run(testname, func(t *testing.T) { + InvertMatch = tt.invert + + readFd := strings.NewReader(table) + gotdata, err := parseFile(readFd, tt.pattern) + + if err != nil { + t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata) + } + + if !reflect.DeepEqual(tt.entries, gotdata.entries) { + t.Errorf("Parser returned invalid data (pattern: %s, invert: %t)\nExp: %+v\nGot: %+v\n", + tt.pattern, tt.invert, tt.entries, gotdata.entries) + } + }) + } +} diff --git a/tablizer.1 b/tablizer.1 index 92a1660..78fab95 100644 --- a/tablizer.1 +++ b/tablizer.1 @@ -133,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "TABLIZER 1" -.TH TABLIZER 1 "2022-10-04" "1" "User Commands" +.TH TABLIZER 1 "2022-10-05" "1" "User Commands" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -150,6 +150,8 @@ tablizer \- Manipulate tabular output of other programs \& \-c, \-\-columns string Only show the speficied columns (separated by ,) \& \-d, \-\-debug Enable debugging \& \-h, \-\-help help for tablizer +\& \-v, \-\-invert\-match select non\-matching rows +\& \-m, \-\-man Display manual page \& \-n, \-\-no\-numbering Disable header numbering \& \-o, \-\-output string Output mode \- one of: orgtbl, markdown, extended, ascii(default) \& \-X, \-\-extended Enable extended output @@ -179,8 +181,9 @@ positions. .PP Without any options it reads its input from \f(CW\*(C`STDIN\*(C'\fR, but you can also specify a file as a parameter. If you want to reduce the output by -some regular expression, just specify it as its first -parameters. Hence: +some regular expression, just specify it as its first parameter. You +may also use the \fB\-v\fR option to exclude all rows which match the +pattern. Hence: .PP .Vb 2 \& # read from STDIN diff --git a/tablizer.pod b/tablizer.pod index 0451bbc..f3137e4 100644 --- a/tablizer.pod +++ b/tablizer.pod @@ -11,6 +11,8 @@ tablizer - Manipulate tabular output of other programs -c, --columns string Only show the speficied columns (separated by ,) -d, --debug Enable debugging -h, --help help for tablizer + -v, --invert-match select non-matching rows + -m, --man Display manual page -n, --no-numbering Disable header numbering -o, --output string Output mode - one of: orgtbl, markdown, extended, ascii(default) -X, --extended Enable extended output @@ -41,8 +43,9 @@ positions. Without any options it reads its input from C, but you can also specify a file as a parameter. If you want to reduce the output by -some regular expression, just specify it as its first -parameters. Hence: +some regular expression, just specify it as its first parameter. You +may also use the B<-v> option to exclude all rows which match the +pattern. Hence: # read from STDIN kubectl get pods | tablizer