add support for negative filters (-F field!=regex)

This commit is contained in:
2025-01-30 17:30:02 +01:00
committed by T.v.Dein
parent 6566dd66f0
commit 2c08687c29
8 changed files with 67 additions and 21 deletions

View File

@@ -22,7 +22,7 @@ Operational Flags:
-s, --separator string Custom field separator -s, --separator string Custom field separator
-k, --sort-by int Sort by column (default: 1) -k, --sort-by int Sort by column (default: 1)
-z, --fuzzy Use fuzzy search [experimental] -z, --fuzzy Use fuzzy search [experimental]
-F, --filter field=reg Filter given field with regex, can be used multiple times -F, --filter field[!]=reg Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,) -T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T -R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T

View File

@@ -1,5 +1,5 @@
/* /*
Copyright © 2022-2024 Thomas von Dein Copyright © 2022-2025 Thomas von Dein
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -58,6 +58,11 @@ type Pattern struct {
Negate bool Negate bool
} }
type Filter struct {
Regex *regexp.Regexp
Negate bool
}
// internal config // internal config
type Config struct { type Config struct {
Debug bool Debug bool
@@ -100,7 +105,7 @@ type Config struct {
// used for field filtering // used for field filtering
Rawfilters []string Rawfilters []string
Filters map[string]*regexp.Regexp Filters map[string]Filter //map[string]*regexp.Regexp
// -r <file> // -r <file>
InputFile string InputFile string
@@ -270,12 +275,20 @@ func (conf *Config) PrepareModeFlags(flag Modeflag) {
} }
func (conf *Config) PrepareFilters() error { func (conf *Config) PrepareFilters() error {
conf.Filters = make(map[string]*regexp.Regexp, len(conf.Rawfilters)) conf.Filters = make(map[string]Filter, len(conf.Rawfilters))
for _, filter := range conf.Rawfilters { for _, rawfilter := range conf.Rawfilters {
parts := strings.Split(filter, "=") filter := Filter{}
parts := strings.Split(rawfilter, "!=")
if len(parts) != MAXPARTS { if len(parts) != MAXPARTS {
return errors.New("filter field and value must be separated by =") parts = strings.Split(rawfilter, "=")
if len(parts) != MAXPARTS {
return errors.New("filter field and value must be separated by '=' or '!='")
}
} else {
filter.Negate = true
} }
reg, err := regexp.Compile(parts[1]) reg, err := regexp.Compile(parts[1])
@@ -284,7 +297,8 @@ func (conf *Config) PrepareFilters() error {
parts[0], err) parts[0], err)
} }
conf.Filters[strings.ToLower(strings.ToLower(parts[0]))] = reg filter.Regex = reg
conf.Filters[strings.ToLower(parts[0])] = filter
} }
return nil return nil

View File

@@ -1,5 +1,5 @@
/* /*
Copyright © 2022-2024 Thomas von Dein Copyright © 2022-2025 Thomas von Dein
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -190,7 +190,7 @@ func Execute() {
// filters // filters
rootCmd.PersistentFlags().StringArrayVarP(&conf.Rawfilters, rootCmd.PersistentFlags().StringArrayVarP(&conf.Rawfilters,
"filter", "F", nil, "Filter by field (field=regexp)") "filter", "F", nil, "Filter by field (field=regexp || field!=regexp)")
rootCmd.PersistentFlags().StringArrayVarP(&conf.Transposers, rootCmd.PersistentFlags().StringArrayVarP(&conf.Transposers,
"regex-transposer", "R", nil, "apply /search/replace/ regexp to fields given in -T") "regex-transposer", "R", nil, "apply /search/replace/ regexp to fields given in -T")

View File

@@ -17,7 +17,7 @@ SYNOPSIS
-s, --separator string Custom field separator -s, --separator string Custom field separator
-k, --sort-by int|name Sort by column (default: 1) -k, --sort-by int|name Sort by column (default: 1)
-z, --fuzzy Use fuzzy search [experimental] -z, --fuzzy Use fuzzy search [experimental]
-F, --filter field=reg Filter given field with regex, can be used multiple times -F, --filter field[!]=reg Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,) -T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T -R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T
@@ -181,6 +181,10 @@ DESCRIPTION
If you specify more than one filter, both filters have to match (AND If you specify more than one filter, both filters have to match (AND
operation). operation).
These field filters can also be negated:
fieldname!=regexp
If the option -v is specified, the filtering is inverted. If the option -v is specified, the filtering is inverted.
COLUMNS COLUMNS
@@ -416,7 +420,7 @@ Operational Flags:
-s, --separator string Custom field separator -s, --separator string Custom field separator
-k, --sort-by int|name Sort by column (default: 1) -k, --sort-by int|name Sort by column (default: 1)
-z, --fuzzy Use fuzzy search [experimental] -z, --fuzzy Use fuzzy search [experimental]
-F, --filter field=reg Filter given field with regex, can be used multiple times -F, --filter field[!]=reg Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,) -T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T -R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T

View File

@@ -1,5 +1,5 @@
/* /*
Copyright © 2022-2024 Thomas von Dein Copyright © 2022-2025 Thomas von Dein
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -86,15 +86,19 @@ func FilterByFields(conf cfg.Config, data *Tabdata) (*Tabdata, bool, error) {
keep := true keep := true
for idx, header := range data.headers { for idx, header := range data.headers {
if !Exists(conf.Filters, strings.ToLower(header)) { lcheader := strings.ToLower(header)
if !Exists(conf.Filters, lcheader) {
// do not filter by unspecified field // do not filter by unspecified field
continue continue
} }
if !conf.Filters[strings.ToLower(header)].MatchString(row[idx]) { match := conf.Filters[lcheader].Regex.MatchString(row[idx])
// there IS a filter, but it doesn't match if conf.Filters[lcheader].Negate {
keep = false match = !match
}
if !match {
keep = false
break break
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
Copyright © 2024 Thomas von Dein Copyright © 2024-2025 Thomas von Dein
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -98,6 +98,20 @@ func TestFilterByFields(t *testing.T) {
}, },
}, },
{
name: "one-field-negative",
filter: []string{"one!=asd"},
expect: Tabdata{
headers: []string{
"ONE", "TWO", "THREE",
},
entries: [][]string{
{"19191", "EDD 1", "x"},
{"8d8", "AN 1", "y"},
},
},
},
{ {
name: "one-field-inverted", name: "one-field-inverted",
filter: []string{"one=19"}, filter: []string{"one=19"},

View File

@@ -133,7 +133,7 @@
.\" ======================================================================== .\" ========================================================================
.\" .\"
.IX Title "TABLIZER 1" .IX Title "TABLIZER 1"
.TH TABLIZER 1 "2025-01-22" "1" "User Commands" .TH TABLIZER 1 "2025-01-30" "1" "User Commands"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents. .\" way too many mistakes in technical documents.
.if n .ad l .if n .ad l
@@ -155,7 +155,7 @@ tablizer \- Manipulate tabular output of other programs
\& \-s, \-\-separator string Custom field separator \& \-s, \-\-separator string Custom field separator
\& \-k, \-\-sort\-by int|name Sort by column (default: 1) \& \-k, \-\-sort\-by int|name Sort by column (default: 1)
\& \-z, \-\-fuzzy Use fuzzy search [experimental] \& \-z, \-\-fuzzy Use fuzzy search [experimental]
\& \-F, \-\-filter field=reg Filter given field with regex, can be used multiple times \& \-F, \-\-filter field[!]=reg Filter given field with regex, can be used multiple times
\& \-T, \-\-transpose\-columns string Transpose the speficied columns (separated by ,) \& \-T, \-\-transpose\-columns string Transpose the speficied columns (separated by ,)
\& \-R, \-\-regex\-transposer /from/to/ Apply /search/replace/ regexp to fields given in \-T \& \-R, \-\-regex\-transposer /from/to/ Apply /search/replace/ regexp to fields given in \-T
\& \&
@@ -340,6 +340,12 @@ Fieldnames (== columns headers) are case insensitive.
If you specify more than one filter, both filters have to match (\s-1AND\s0 If you specify more than one filter, both filters have to match (\s-1AND\s0
operation). operation).
.PP .PP
These field filters can also be negated:
.PP
.Vb 1
\& fieldname!=regexp
.Ve
.PP
If the option \fB\-v\fR is specified, the filtering is inverted. If the option \fB\-v\fR is specified, the filtering is inverted.
.SS "\s-1COLUMNS\s0" .SS "\s-1COLUMNS\s0"
.IX Subsection "COLUMNS" .IX Subsection "COLUMNS"

View File

@@ -16,7 +16,7 @@ tablizer - Manipulate tabular output of other programs
-s, --separator string Custom field separator -s, --separator string Custom field separator
-k, --sort-by int|name Sort by column (default: 1) -k, --sort-by int|name Sort by column (default: 1)
-z, --fuzzy Use fuzzy search [experimental] -z, --fuzzy Use fuzzy search [experimental]
-F, --filter field=reg Filter given field with regex, can be used multiple times -F, --filter field[!]=reg Filter given field with regex, can be used multiple times
-T, --transpose-columns string Transpose the speficied columns (separated by ,) -T, --transpose-columns string Transpose the speficied columns (separated by ,)
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T -R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T
@@ -194,6 +194,10 @@ Fieldnames (== columns headers) are case insensitive.
If you specify more than one filter, both filters have to match (AND If you specify more than one filter, both filters have to match (AND
operation). operation).
These field filters can also be negated:
fieldname!=regexp
If the option B<-v> is specified, the filtering is inverted. If the option B<-v> is specified, the filtering is inverted.