mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-18 13:01:11 +01:00
Compare commits
4 Commits
feature/ya
...
feature/mu
| Author | SHA1 | Date | |
|---|---|---|---|
| 479d8c274a | |||
| a49e28518b | |||
| e29e72b7d2 | |||
| f6e3075ea8 |
2
Makefile
2
Makefile
@@ -69,7 +69,7 @@ test:
|
||||
|
||||
singletest:
|
||||
@echo "Call like this: 'make singletest TEST=TestPrepareColumns MOD=lib'"
|
||||
go test -run $(TEST) github.com/tlinden/tablizer/$(MOD) $(OPTS)
|
||||
go test -run $(TEST) github.com/tlinden/tablizer/$(MOD)
|
||||
|
||||
cover-report:
|
||||
go test ./... -cover -coverprofile=coverage.out
|
||||
|
||||
@@ -35,8 +35,6 @@ Output Flags (mutually exclusive):
|
||||
-C, --csv Enable CSV output
|
||||
-A, --ascii Default output mode, ascii tabular
|
||||
-L, --hightlight-lines Use alternating background colors for tables
|
||||
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
|
||||
space separated
|
||||
|
||||
Sort Mode Flags (mutually exclusive):
|
||||
-a, --sort-age sort according to age (duration) string
|
||||
|
||||
@@ -52,6 +52,12 @@ type Transposer struct {
|
||||
Replace string
|
||||
}
|
||||
|
||||
type Pattern struct {
|
||||
Pattern string
|
||||
PatternRe *regexp.Regexp
|
||||
Negate bool
|
||||
}
|
||||
|
||||
// internal config
|
||||
type Config struct {
|
||||
Debug bool
|
||||
@@ -59,13 +65,10 @@ type Config struct {
|
||||
NoHeaders bool
|
||||
Columns string
|
||||
UseColumns []int
|
||||
YankColumns string
|
||||
UseYankColumns []int
|
||||
Separator string
|
||||
OutputMode int
|
||||
InvertMatch bool
|
||||
Pattern string
|
||||
PatternR *regexp.Regexp
|
||||
Patterns []*Pattern
|
||||
UseFuzzySearch bool
|
||||
UseHighlight bool
|
||||
|
||||
@@ -335,15 +338,37 @@ func (conf *Config) ApplyDefaults() {
|
||||
}
|
||||
}
|
||||
|
||||
func (conf *Config) PreparePattern(pattern string) error {
|
||||
PatternR, err := regexp.Compile(pattern)
|
||||
func (conf *Config) PreparePattern(patterns []*Pattern) error {
|
||||
// regex checks if a pattern looks like /$pattern/[i!]
|
||||
flagre := regexp.MustCompile(`^/(.*)/([i!]*)$`)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("regexp pattern %s is invalid: %w", conf.Pattern, err)
|
||||
for _, pattern := range patterns {
|
||||
matches := flagre.FindAllStringSubmatch(pattern.Pattern, -1)
|
||||
|
||||
// we have a regex with flags
|
||||
for _, match := range matches {
|
||||
pattern.Pattern = match[1] // the inner part is our actual pattern
|
||||
flags := match[2] // the flags
|
||||
|
||||
for _, flag := range flags {
|
||||
switch flag {
|
||||
case 'i':
|
||||
pattern.Pattern = `(?i)` + pattern.Pattern
|
||||
case '!':
|
||||
pattern.Negate = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PatternRe, err := regexp.Compile(pattern.Pattern)
|
||||
if err != nil {
|
||||
return fmt.Errorf("regexp pattern %s is invalid: %w", pattern.Pattern, err)
|
||||
}
|
||||
|
||||
pattern.PatternRe = PatternRe
|
||||
}
|
||||
|
||||
conf.PatternR = PatternR
|
||||
conf.Pattern = pattern
|
||||
conf.Patterns = patterns
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -79,20 +79,55 @@ func TestPrepareSortFlags(t *testing.T) {
|
||||
|
||||
func TestPreparePattern(t *testing.T) {
|
||||
var tests = []struct {
|
||||
pattern string
|
||||
wanterr bool
|
||||
patterns []*Pattern
|
||||
name string
|
||||
wanterr bool
|
||||
wanticase bool
|
||||
wantneg bool
|
||||
}{
|
||||
{"[A-Z]+", false},
|
||||
{"[a-z", true},
|
||||
{
|
||||
[]*Pattern{{Pattern: "[A-Z]+"}},
|
||||
"simple",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]*Pattern{{Pattern: "[a-z"}},
|
||||
"regfail",
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]*Pattern{{Pattern: "/[A-Z]+/i"}},
|
||||
"icase",
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]*Pattern{{Pattern: "/[A-Z]+/!"}},
|
||||
"negate",
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]*Pattern{{Pattern: "/[A-Z]+/!i"}},
|
||||
"negicase",
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testdata := range tests {
|
||||
testname := fmt.Sprintf("PreparePattern-pattern-%s-wanterr-%t",
|
||||
testdata.pattern, testdata.wanterr)
|
||||
testname := fmt.Sprintf("PreparePattern-pattern-%s-wanterr-%t", testdata.name, testdata.wanterr)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
conf := Config{}
|
||||
|
||||
err := conf.PreparePattern(testdata.pattern)
|
||||
err := conf.PreparePattern(testdata.patterns)
|
||||
|
||||
if err != nil {
|
||||
if !testdata.wanterr {
|
||||
|
||||
@@ -147,8 +147,6 @@ func Execute() {
|
||||
"Custom field separator")
|
||||
rootCmd.PersistentFlags().StringVarP(&conf.Columns, "columns", "c", "",
|
||||
"Only show the speficied columns (separated by ,)")
|
||||
rootCmd.PersistentFlags().StringVarP(&conf.YankColumns, "yank-columns", "y", "",
|
||||
"Yank the speficied columns (separated by ,) to the clipboard")
|
||||
rootCmd.PersistentFlags().StringVarP(&conf.TransposeColumns, "transpose-columns", "T", "",
|
||||
"Transpose the speficied columns (separated by ,)")
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ NAME
|
||||
|
||||
SYNOPSIS
|
||||
Usage:
|
||||
tablizer [regex] [file, ...] [flags]
|
||||
tablizer [regex,...] [file, ...] [flags]
|
||||
|
||||
Operational Flags:
|
||||
-c, --columns string Only show the speficied columns (separated by ,)
|
||||
@@ -30,8 +30,6 @@ SYNOPSIS
|
||||
-C, --csv Enable CSV output
|
||||
-A, --ascii Default output mode, ascii tabular
|
||||
-L, --hightlight-lines Use alternating background colors for tables
|
||||
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
|
||||
space separated
|
||||
|
||||
Sort Mode Flags (mutually exclusive):
|
||||
-a, --sort-age sort according to age (duration) string
|
||||
@@ -132,30 +130,43 @@ DESCRIPTION
|
||||
for the developer.
|
||||
|
||||
PATTERNS AND FILTERING
|
||||
You can reduce the rows being displayed by using a regular expression
|
||||
pattern. The regexp is PCRE compatible, refer to the syntax cheat sheet
|
||||
here: <https://github.com/google/re2/wiki/Syntax>. If you want to read a
|
||||
more comprehensive documentation about the topic and have perl installed
|
||||
you can read it with:
|
||||
You can reduce the rows being displayed by using one or more regular
|
||||
expression patterns. The regexp language being used is the one of
|
||||
GOLANG, refer to the syntax cheat sheet here:
|
||||
<https://pkg.go.dev/regexp/syntax>.
|
||||
|
||||
If you want to read a more comprehensive documentation about the topic
|
||||
and have perl installed you can read it with:
|
||||
|
||||
perldoc perlre
|
||||
|
||||
Or read it online: <https://perldoc.perl.org/perlre>.
|
||||
Or read it online: <https://perldoc.perl.org/perlre>. But please note
|
||||
that the GO regexp engine does NOT support all perl regex terms,
|
||||
especially look-ahead and look-behind.
|
||||
|
||||
A note on modifiers: the regexp engine used in tablizer uses another
|
||||
modifier syntax:
|
||||
If you want to supply flags to a regex, then surround it with slashes
|
||||
and append the flag. The following flags are supported:
|
||||
|
||||
(?MODIFIER)
|
||||
|
||||
The most important modifiers are:
|
||||
|
||||
"i" ignore case "m" multiline mode "s" single line mode
|
||||
i => case insensitive
|
||||
! => negative match
|
||||
|
||||
Example for a case insensitive search:
|
||||
|
||||
kubectl get pods -A | tablizer "(?i)account"
|
||||
kubectl get pods -A | tablizer "/account/i"
|
||||
|
||||
You can use the experimental fuzzy search feature by providing the
|
||||
If you use the "!" flag, then the regex match will be negated, that is,
|
||||
if a line in the input matches the given regex, but "!" is supplied,
|
||||
tablizer will NOT include it in the output.
|
||||
|
||||
For example, here we want to get all lines matching "foo" but not "bar":
|
||||
|
||||
cat table | tablizer foo '/bar/!'
|
||||
|
||||
This would match a line "foo zorro" but not "foo bar".
|
||||
|
||||
The flags can also be combined.
|
||||
|
||||
You can also use the experimental fuzzy search feature by providing the
|
||||
option -z, in which case the pattern is regarded as a fuzzy search term,
|
||||
not a regexp.
|
||||
|
||||
@@ -269,18 +280,6 @@ DESCRIPTION
|
||||
markdown which prints a Markdown table, yaml, which prints yaml encoding
|
||||
and CSV mode, which prints a comma separated value file.
|
||||
|
||||
PUT FIELDS TO CLIPBOARD
|
||||
You can let tablizer put fields to the clipboard using the option "-y".
|
||||
This best fits the use-case when the result of your filtering yields
|
||||
just one row. For example:
|
||||
|
||||
cloudctl cluster ls | tablizer -yid matchbox
|
||||
|
||||
If "matchbox" matches one cluster, you can immediately use the id of
|
||||
that cluster somewhere else and paste it. Of course, if there are
|
||||
multiple matches, then all id's will be put into the clipboard separated
|
||||
by one space.
|
||||
|
||||
ENVIRONMENT VARIABLES
|
||||
tablizer supports certain environment variables which use can use to
|
||||
influence program behavior. Commandline flags have always precedence
|
||||
@@ -406,7 +405,7 @@ AUTHORS
|
||||
var usage = `
|
||||
|
||||
Usage:
|
||||
tablizer [regex] [file, ...] [flags]
|
||||
tablizer [regex,...] [file, ...] [flags]
|
||||
|
||||
Operational Flags:
|
||||
-c, --columns string Only show the speficied columns (separated by ,)
|
||||
@@ -430,8 +429,6 @@ Output Flags (mutually exclusive):
|
||||
-C, --csv Enable CSV output
|
||||
-A, --ascii Default output mode, ascii tabular
|
||||
-L, --hightlight-lines Use alternating background colors for tables
|
||||
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
|
||||
space separated
|
||||
|
||||
Sort Mode Flags (mutually exclusive):
|
||||
-a, --sort-age sort according to age (duration) string
|
||||
|
||||
3
go.mod
3
go.mod
@@ -5,7 +5,6 @@ go 1.22
|
||||
require (
|
||||
github.com/alecthomas/repr v0.4.0
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/hashicorp/hcl/v2 v2.23.0
|
||||
github.com/lithammer/fuzzysearch v1.1.8
|
||||
@@ -23,10 +22,8 @@ require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/tiagomelo/go-clipboard v0.1.2-0.20250126153310-fcc1f95408cf // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/zclconf/go-cty v1.13.3 // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
|
||||
8
go.sum
8
go.sum
@@ -8,8 +8,6 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -34,8 +32,6 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzC
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
@@ -53,10 +49,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tiagomelo/go-clipboard v0.1.1 h1:nddQ5DsEnKW0KdzTILhbLpSq3e9y2dkJXEOtsMs6H7A=
|
||||
github.com/tiagomelo/go-clipboard v0.1.1/go.mod h1:kXtjJBIMimZaGbxmcKZ8+JqK+acSNf5tAJiChlZBOr8=
|
||||
github.com/tiagomelo/go-clipboard v0.1.2-0.20250126153310-fcc1f95408cf h1:csb/+rmJBAtOP6OP+9soTnwJVuhlUpedjb5dPlNZasY=
|
||||
github.com/tiagomelo/go-clipboard v0.1.2-0.20250126153310-fcc1f95408cf/go.mod h1:kXtjJBIMimZaGbxmcKZ8+JqK+acSNf5tAJiChlZBOr8=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
|
||||
@@ -27,15 +27,46 @@ import (
|
||||
)
|
||||
|
||||
/*
|
||||
* [!]Match a line, use fuzzy search for normal pattern strings and
|
||||
* regexp otherwise.
|
||||
*/
|
||||
* [!]Match a line, use fuzzy search for normal pattern strings and
|
||||
* regexp otherwise.
|
||||
|
||||
'foo bar' foo, /bar/! => false => line contains foo and not (not bar)
|
||||
'foo nix' foo, /bar/! => ture => line contains foo and (not bar)
|
||||
'foo bar' foo, /bar/ => true => line contains both foo and bar
|
||||
'foo nix' foo, /bar/ => false => line does not contain bar
|
||||
'foo bar' foo, /nix/ => false => line does not contain nix
|
||||
*/
|
||||
func matchPattern(conf cfg.Config, line string) bool {
|
||||
if conf.UseFuzzySearch {
|
||||
return fuzzy.MatchFold(conf.Pattern, line)
|
||||
if len(conf.Patterns) == 0 {
|
||||
// any line always matches ""
|
||||
return true
|
||||
}
|
||||
|
||||
return conf.PatternR.MatchString(line)
|
||||
if conf.UseFuzzySearch {
|
||||
// fuzzy search only considers the 1st pattern
|
||||
return fuzzy.MatchFold(conf.Patterns[0].Pattern, line)
|
||||
}
|
||||
|
||||
var match int
|
||||
|
||||
//fmt.Printf("<%s>\n", line)
|
||||
for _, re := range conf.Patterns {
|
||||
patmatch := re.PatternRe.MatchString(line)
|
||||
if re.Negate {
|
||||
// toggle the meaning of match
|
||||
patmatch = !patmatch
|
||||
}
|
||||
|
||||
if patmatch {
|
||||
match++
|
||||
}
|
||||
|
||||
//fmt.Printf("patmatch: %t, match: %d, pattern: %s, negate: %t\n", patmatch, match, re.Pattern, re.Negate)
|
||||
}
|
||||
|
||||
// fmt.Printf("result: %t\n", match == len(conf.Patterns))
|
||||
//fmt.Println()
|
||||
return match == len(conf.Patterns)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -123,8 +154,11 @@ func Exists[K comparable, V any](m map[K]V, v K) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
* Filters the whole input lines, returns filtered lines
|
||||
*/
|
||||
func FilterByPattern(conf cfg.Config, input io.Reader) (io.Reader, error) {
|
||||
if conf.Pattern == "" {
|
||||
if len(conf.Patterns) == 0 {
|
||||
return input, nil
|
||||
}
|
||||
|
||||
@@ -136,7 +170,7 @@ func FilterByPattern(conf cfg.Config, input io.Reader) (io.Reader, error) {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if hadFirst {
|
||||
// don't match 1st line, it's the header
|
||||
if conf.Pattern != "" && matchPattern(conf, line) == conf.InvertMatch {
|
||||
if matchPattern(conf, line) == conf.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,
|
||||
|
||||
@@ -27,21 +27,21 @@ import (
|
||||
|
||||
func TestMatchPattern(t *testing.T) {
|
||||
var input = []struct {
|
||||
name string
|
||||
fuzzy bool
|
||||
pattern string
|
||||
line string
|
||||
name string
|
||||
fuzzy bool
|
||||
patterns []*cfg.Pattern
|
||||
line string
|
||||
}{
|
||||
{
|
||||
name: "normal",
|
||||
pattern: "haus",
|
||||
line: "hausparty",
|
||||
name: "normal",
|
||||
patterns: []*cfg.Pattern{{Pattern: "haus"}},
|
||||
line: "hausparty",
|
||||
},
|
||||
{
|
||||
name: "fuzzy",
|
||||
pattern: "hpt",
|
||||
line: "haus-party-termin",
|
||||
fuzzy: true,
|
||||
name: "fuzzy",
|
||||
patterns: []*cfg.Pattern{{Pattern: "hpt"}},
|
||||
line: "haus-party-termin",
|
||||
fuzzy: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func TestMatchPattern(t *testing.T) {
|
||||
conf.UseFuzzySearch = true
|
||||
}
|
||||
|
||||
err := conf.PreparePattern(inputdata.pattern)
|
||||
err := conf.PreparePattern(inputdata.patterns)
|
||||
if err != nil {
|
||||
t.Errorf("PreparePattern returned error: %s", err)
|
||||
}
|
||||
|
||||
@@ -77,14 +77,6 @@ func PrepareColumns(conf *cfg.Config, data *Tabdata) error {
|
||||
|
||||
conf.UseColumns = usecolumns
|
||||
|
||||
// -y columns
|
||||
useyankcolumns, err := PrepareColumnVars(conf.YankColumns, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conf.UseYankColumns = useyankcolumns
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -301,12 +293,20 @@ func colorizeData(conf cfg.Config, output string) string {
|
||||
|
||||
return colorized
|
||||
|
||||
case len(conf.Pattern) > 0 && !conf.NoColor && color.IsConsole(os.Stdout):
|
||||
r := regexp.MustCompile("(" + conf.Pattern + ")")
|
||||
case len(conf.Patterns) > 0 && !conf.NoColor && color.IsConsole(os.Stdout):
|
||||
out := output
|
||||
|
||||
return r.ReplaceAllStringFunc(output, func(in string) string {
|
||||
return conf.ColorStyle.Sprint(in)
|
||||
})
|
||||
for _, re := range conf.Patterns {
|
||||
if !re.Negate {
|
||||
r := regexp.MustCompile("(" + re.Pattern + ")")
|
||||
|
||||
out = r.ReplaceAllStringFunc(out, func(in string) string {
|
||||
return conf.ColorStyle.Sprint(in)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
|
||||
default:
|
||||
return output
|
||||
|
||||
20
lib/io.go
20
lib/io.go
@@ -29,13 +29,13 @@ import (
|
||||
const RWRR = 0755
|
||||
|
||||
func ProcessFiles(conf *cfg.Config, args []string) error {
|
||||
fd, pattern, err := determineIO(conf, args)
|
||||
fd, patterns, err := determineIO(conf, args)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := conf.PreparePattern(pattern); err != nil {
|
||||
if err := conf.PreparePattern(patterns); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -63,9 +63,9 @@ func ProcessFiles(conf *cfg.Config, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func determineIO(conf *cfg.Config, args []string) (io.Reader, string, error) {
|
||||
func determineIO(conf *cfg.Config, args []string) (io.Reader, []*cfg.Pattern, error) {
|
||||
var filehandle io.Reader
|
||||
var pattern string
|
||||
var patterns []*cfg.Pattern
|
||||
var haveio bool
|
||||
|
||||
switch {
|
||||
@@ -76,7 +76,7 @@ func determineIO(conf *cfg.Config, args []string) (io.Reader, string, error) {
|
||||
fd, err := os.OpenFile(conf.InputFile, os.O_RDONLY, RWRR)
|
||||
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to read input file %s: %w", conf.InputFile, err)
|
||||
return nil, nil, fmt.Errorf("failed to read input file %s: %w", conf.InputFile, err)
|
||||
}
|
||||
|
||||
filehandle = fd
|
||||
@@ -93,13 +93,15 @@ func determineIO(conf *cfg.Config, args []string) (io.Reader, string, error) {
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
pattern = args[0]
|
||||
conf.Pattern = args[0]
|
||||
patterns = make([]*cfg.Pattern, len(args))
|
||||
for i, arg := range args {
|
||||
patterns[i] = &cfg.Pattern{Pattern: arg}
|
||||
}
|
||||
}
|
||||
|
||||
if !haveio {
|
||||
return nil, "", errors.New("no file specified and nothing to read on stdin")
|
||||
return nil, nil, errors.New("no file specified and nothing to read on stdin")
|
||||
}
|
||||
|
||||
return filehandle, pattern, nil
|
||||
return filehandle, patterns, nil
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ func parseTabular(conf cfg.Config, input io.Reader) (Tabdata, error) {
|
||||
}
|
||||
} else {
|
||||
// data processing
|
||||
if conf.Pattern != "" && matchPattern(conf, line) == conf.InvertMatch {
|
||||
if matchPattern(conf, line) == conf.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,
|
||||
|
||||
@@ -83,36 +83,42 @@ func TestParser(t *testing.T) {
|
||||
|
||||
func TestParserPatternmatching(t *testing.T) {
|
||||
var tests = []struct {
|
||||
entries [][]string
|
||||
pattern string
|
||||
invert bool
|
||||
want bool
|
||||
name string
|
||||
entries [][]string
|
||||
patterns []*cfg.Pattern
|
||||
invert bool
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "match",
|
||||
entries: [][]string{
|
||||
{"asd", "igig", "cxxxncnc"},
|
||||
},
|
||||
pattern: "ig",
|
||||
invert: false,
|
||||
patterns: []*cfg.Pattern{{Pattern: "ig"}},
|
||||
invert: false,
|
||||
},
|
||||
{
|
||||
name: "invert",
|
||||
entries: [][]string{
|
||||
{"19191", "EDD 1", "X"},
|
||||
},
|
||||
pattern: "ig",
|
||||
invert: true,
|
||||
patterns: []*cfg.Pattern{{Pattern: "ig"}},
|
||||
invert: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, inputdata := range input {
|
||||
for _, testdata := range tests {
|
||||
testname := fmt.Sprintf("parse-%s-with-pattern-%s-inverted-%t",
|
||||
inputdata.name, testdata.pattern, testdata.invert)
|
||||
inputdata.name, testdata.name, testdata.invert)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
conf := cfg.Config{InvertMatch: testdata.invert, Pattern: testdata.pattern,
|
||||
Separator: inputdata.separator}
|
||||
conf := cfg.Config{
|
||||
InvertMatch: testdata.invert,
|
||||
Patterns: testdata.patterns,
|
||||
Separator: inputdata.separator,
|
||||
}
|
||||
|
||||
_ = conf.PreparePattern(testdata.pattern)
|
||||
_ = conf.PreparePattern(testdata.patterns)
|
||||
|
||||
readFd := strings.NewReader(strings.TrimSpace(inputdata.text))
|
||||
gotdata, err := Parse(conf, readFd)
|
||||
@@ -125,7 +131,7 @@ func TestParserPatternmatching(t *testing.T) {
|
||||
} else {
|
||||
if !reflect.DeepEqual(testdata.entries, gotdata.entries) {
|
||||
t.Errorf("Parser returned invalid data (pattern: %s, invert: %t)\nExp: %+v\nGot: %+v\n",
|
||||
testdata.pattern, testdata.invert, testdata.entries, gotdata.entries)
|
||||
testdata.name, testdata.invert, testdata.entries, gotdata.entries)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright © 2022-2025 Thomas von Dein
|
||||
Copyright © 2022 Thomas von Dein
|
||||
|
||||
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
|
||||
@@ -38,9 +38,6 @@ func printData(writer io.Writer, conf cfg.Config, data *Tabdata) {
|
||||
// by, independently if it's being used for display or not.
|
||||
sortTable(conf, data)
|
||||
|
||||
// put one or more columns into clipboard
|
||||
yankColumns(conf, data)
|
||||
|
||||
// add numbers to headers and remove those we're not interested in
|
||||
numberizeAndReduceHeaders(conf, data)
|
||||
|
||||
|
||||
51
lib/yank.go
51
lib/yank.go
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
Copyright © 2022-2025 Thomas von Dein
|
||||
|
||||
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
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package lib
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/tiagomelo/go-clipboard/clipboard"
|
||||
"github.com/tlinden/tablizer/cfg"
|
||||
)
|
||||
|
||||
func yankColumns(conf cfg.Config, data *Tabdata) {
|
||||
var yank []string
|
||||
|
||||
if len(data.entries) == 0 || len(conf.UseYankColumns) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, row := range data.entries {
|
||||
for i, field := range row {
|
||||
for _, idx := range conf.UseYankColumns {
|
||||
if i == idx-1 {
|
||||
yank = append(yank, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(yank) > 0 {
|
||||
cb := clipboard.New(clipboard.ClipboardOptions{Primary: true})
|
||||
if err := cb.CopyText(strings.Join(yank, " ")); err != nil {
|
||||
log.Fatalln("error writing string to clipboard:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
Copyright © 2025 Thomas von Dein
|
||||
|
||||
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
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package lib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/tiagomelo/go-clipboard/clipboard"
|
||||
"github.com/tlinden/tablizer/cfg"
|
||||
)
|
||||
|
||||
var yanktests = []struct {
|
||||
name string
|
||||
yank []int // -y$colum,$column... after processing
|
||||
filter string
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "one",
|
||||
yank: []int{1},
|
||||
filter: "beta",
|
||||
},
|
||||
}
|
||||
|
||||
func DISABLED_TestYankColumns(t *testing.T) {
|
||||
cb := clipboard.New()
|
||||
|
||||
for _, testdata := range yanktests {
|
||||
testname := fmt.Sprintf("yank-%s-filter-%s",
|
||||
testdata.name, testdata.filter)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
conf := cfg.Config{
|
||||
OutputMode: cfg.ASCII,
|
||||
UseYankColumns: testdata.yank,
|
||||
NoColor: true,
|
||||
}
|
||||
|
||||
conf.ApplyDefaults()
|
||||
data := newData() // defined in printer_test.go, reused here
|
||||
|
||||
var writer bytes.Buffer
|
||||
printData(&writer, conf, &data)
|
||||
|
||||
got, err := cb.PasteText()
|
||||
if err != nil {
|
||||
t.Errorf("failed to fetch yanked text from clipboard")
|
||||
}
|
||||
|
||||
if got != testdata.expect {
|
||||
t.Errorf("not yanked correctly:\n+++ got:\n%s\n+++ want:\n%s",
|
||||
got, testdata.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
46
t/test-multipatterns.txtar
Normal file
46
t/test-multipatterns.txtar
Normal file
@@ -0,0 +1,46 @@
|
||||
# filtering
|
||||
|
||||
# a AND b
|
||||
exec tablizer -r testtable.txt -H -cspecies invasive imperium
|
||||
stdout 'namak'
|
||||
! stdout human
|
||||
|
||||
# a AND !b
|
||||
exec tablizer -r testtable.txt -H -cspecies invasive '/imperium/!'
|
||||
stdout 'human'
|
||||
! stdout namak
|
||||
|
||||
# a AND !b AND c
|
||||
exec tablizer -r testtable.txt -H -cspecies peaceful '/imperium/!' planetary
|
||||
stdout 'kenaha'
|
||||
! stdout 'namak|heduu|riedl'
|
||||
|
||||
# case insensitive
|
||||
exec tablizer -r testtable.txt -H -cspecies '/REGIONAL/i'
|
||||
stdout namak
|
||||
! stdout 'human|riedl|heduu|kenaa'
|
||||
|
||||
# case insensitive negated
|
||||
exec tablizer -r testtable.txt -H -cspecies '/REGIONAL/!i'
|
||||
stdout 'human|riedl|heduu|kenaa'
|
||||
! stdout namak
|
||||
|
||||
# !a AND !b
|
||||
exec tablizer -r testtable.txt -H -cspecies '/galactic/!' '/planetary/!'
|
||||
stdout namak
|
||||
! stdout 'human|riedl|heduu|kenaa'
|
||||
|
||||
# same case insensitive
|
||||
exec tablizer -r testtable.txt -H -cspecies '/GALACTIC/i!' '/PLANETARY/!i'
|
||||
stdout namak
|
||||
! stdout 'human|riedl|heduu|kenaa'
|
||||
|
||||
# will be automatically created in work dir
|
||||
-- testtable.txt --
|
||||
SPECIES TYPE HOME STAGE SPREAD
|
||||
human invasive earth brink planetary
|
||||
riedl peaceful keauna civilized pangalactic
|
||||
namak invasive namak imperium regional
|
||||
heduu peaceful iu imperium galactic
|
||||
kenaha peaceful kohi hunter-gatherer planetary
|
||||
|
||||
6
t/testtable5
Normal file
6
t/testtable5
Normal file
@@ -0,0 +1,6 @@
|
||||
SPECIES TYPE HOME STAGE
|
||||
human invasive earth brink
|
||||
riedl peaceful keauna civilized
|
||||
namak invasive namak imperium
|
||||
heduu peaceful iu imperium
|
||||
kenaha peaceful kohi hunter-gatherer
|
||||
70
tablizer.1
70
tablizer.1
@@ -133,7 +133,7 @@
|
||||
.\" ========================================================================
|
||||
.\"
|
||||
.IX Title "TABLIZER 1"
|
||||
.TH TABLIZER 1 "2025-01-23" "1" "User Commands"
|
||||
.TH TABLIZER 1 "2025-01-22" "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
|
||||
@@ -144,7 +144,7 @@ tablizer \- Manipulate tabular output of other programs
|
||||
.IX Header "SYNOPSIS"
|
||||
.Vb 2
|
||||
\& Usage:
|
||||
\& tablizer [regex] [file, ...] [flags]
|
||||
\& tablizer [regex,...] [file, ...] [flags]
|
||||
\&
|
||||
\& Operational Flags:
|
||||
\& \-c, \-\-columns string Only show the speficied columns (separated by ,)
|
||||
@@ -168,8 +168,6 @@ tablizer \- Manipulate tabular output of other programs
|
||||
\& \-C, \-\-csv Enable CSV output
|
||||
\& \-A, \-\-ascii Default output mode, ascii tabular
|
||||
\& \-L, \-\-hightlight\-lines Use alternating background colors for tables
|
||||
\& \-y, \-\-yank\-columns Yank specified columns (separated by ,) to clipboard,
|
||||
\& space separated
|
||||
\&
|
||||
\& Sort Mode Flags (mutually exclusive):
|
||||
\& \-a, \-\-sort\-age sort according to age (duration) string
|
||||
@@ -280,38 +278,52 @@ Finally the \fB\-d\fR option enables debugging output which is mostly
|
||||
useful for the developer.
|
||||
.SS "\s-1PATTERNS AND FILTERING\s0"
|
||||
.IX Subsection "PATTERNS AND FILTERING"
|
||||
You can reduce the rows being displayed by using a regular expression
|
||||
pattern. The regexp is \s-1PCRE\s0 compatible, refer to the syntax cheat
|
||||
sheet here: <https://github.com/google/re2/wiki/Syntax>. If you want
|
||||
to read a more comprehensive documentation about the topic and have
|
||||
perl installed you can read it with:
|
||||
You can reduce the rows being displayed by using one or more regular
|
||||
expression patterns. The regexp language being used is the one of
|
||||
\&\s-1GOLANG,\s0 refer to the syntax cheat sheet here:
|
||||
<https://pkg.go.dev/regexp/syntax>.
|
||||
.PP
|
||||
If you want to read a more comprehensive documentation about the
|
||||
topic and have perl installed you can read it with:
|
||||
.PP
|
||||
.Vb 1
|
||||
\& perldoc perlre
|
||||
.Ve
|
||||
.PP
|
||||
Or read it online: <https://perldoc.perl.org/perlre>.
|
||||
Or read it online: <https://perldoc.perl.org/perlre>. But please note
|
||||
that the \s-1GO\s0 regexp engine does \s-1NOT\s0 support all perl regex terms,
|
||||
especially look-ahead and look-behind.
|
||||
.PP
|
||||
A note on modifiers: the regexp engine used in tablizer uses another
|
||||
modifier syntax:
|
||||
If you want to supply flags to a regex, then surround it with slashes
|
||||
and append the flag. The following flags are supported:
|
||||
.PP
|
||||
.Vb 1
|
||||
\& (?MODIFIER)
|
||||
.Vb 2
|
||||
\& i => case insensitive
|
||||
\& ! => negative match
|
||||
.Ve
|
||||
.PP
|
||||
The most important modifiers are:
|
||||
.PP
|
||||
\&\f(CW\*(C`i\*(C'\fR ignore case
|
||||
\&\f(CW\*(C`m\*(C'\fR multiline mode
|
||||
\&\f(CW\*(C`s\*(C'\fR single line mode
|
||||
.PP
|
||||
Example for a case insensitive search:
|
||||
.PP
|
||||
.Vb 1
|
||||
\& kubectl get pods \-A | tablizer "(?i)account"
|
||||
\& kubectl get pods \-A | tablizer "/account/i"
|
||||
.Ve
|
||||
.PP
|
||||
You can use the experimental fuzzy search feature by providing the
|
||||
If you use the \f(CW\*(C`!\*(C'\fR flag, then the regex match will be negated, that
|
||||
is, if a line in the input matches the given regex, but \f(CW\*(C`!\*(C'\fR is
|
||||
supplied, tablizer will \s-1NOT\s0 include it in the output.
|
||||
.PP
|
||||
For example, here we want to get all lines matching \*(L"foo\*(R" but not
|
||||
\&\*(L"bar\*(R":
|
||||
.PP
|
||||
.Vb 1
|
||||
\& cat table | tablizer foo \*(Aq/bar/!\*(Aq
|
||||
.Ve
|
||||
.PP
|
||||
This would match a line \*(L"foo zorro\*(R" but not \*(L"foo bar\*(R".
|
||||
.PP
|
||||
The flags can also be combined.
|
||||
.PP
|
||||
You can also use the experimental fuzzy search feature by providing the
|
||||
option \fB\-z\fR, in which case the pattern is regarded as a fuzzy search
|
||||
term, not a regexp.
|
||||
.PP
|
||||
@@ -444,20 +456,6 @@ more output modes available: \fBorgtbl\fR which prints an Emacs org-mode
|
||||
table and \fBmarkdown\fR which prints a Markdown table, \fByaml\fR, which
|
||||
prints yaml encoding and \s-1CSV\s0 mode, which prints a comma separated
|
||||
value file.
|
||||
.SS "\s-1PUT FIELDS TO CLIPBOARD\s0"
|
||||
.IX Subsection "PUT FIELDS TO CLIPBOARD"
|
||||
You can let tablizer put fields to the clipboard using the option
|
||||
\&\f(CW\*(C`\-y\*(C'\fR. This best fits the use-case when the result of your filtering
|
||||
yields just one row. For example:
|
||||
.PP
|
||||
.Vb 1
|
||||
\& cloudctl cluster ls | tablizer \-yid matchbox
|
||||
.Ve
|
||||
.PP
|
||||
If \*(L"matchbox\*(R" matches one cluster, you can immediately use the id of
|
||||
that cluster somewhere else and paste it. Of course, if there are
|
||||
multiple matches, then all id's will be put into the clipboard
|
||||
separated by one space.
|
||||
.SS "\s-1ENVIRONMENT VARIABLES\s0"
|
||||
.IX Subsection "ENVIRONMENT VARIABLES"
|
||||
\&\fBtablizer\fR supports certain environment variables which use can use
|
||||
|
||||
63
tablizer.pod
63
tablizer.pod
@@ -5,7 +5,7 @@ tablizer - Manipulate tabular output of other programs
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage:
|
||||
tablizer [regex] [file, ...] [flags]
|
||||
tablizer [regex,...] [file, ...] [flags]
|
||||
|
||||
Operational Flags:
|
||||
-c, --columns string Only show the speficied columns (separated by ,)
|
||||
@@ -29,8 +29,6 @@ tablizer - Manipulate tabular output of other programs
|
||||
-C, --csv Enable CSV output
|
||||
-A, --ascii Default output mode, ascii tabular
|
||||
-L, --hightlight-lines Use alternating background colors for tables
|
||||
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
|
||||
space separated
|
||||
|
||||
Sort Mode Flags (mutually exclusive):
|
||||
-a, --sort-age sort according to age (duration) string
|
||||
@@ -144,32 +142,44 @@ useful for the developer.
|
||||
|
||||
=head2 PATTERNS AND FILTERING
|
||||
|
||||
You can reduce the rows being displayed by using a regular expression
|
||||
pattern. The regexp is PCRE compatible, refer to the syntax cheat
|
||||
sheet here: L<https://github.com/google/re2/wiki/Syntax>. If you want
|
||||
to read a more comprehensive documentation about the topic and have
|
||||
perl installed you can read it with:
|
||||
You can reduce the rows being displayed by using one or more regular
|
||||
expression patterns. The regexp language being used is the one of
|
||||
GOLANG, refer to the syntax cheat sheet here:
|
||||
L<https://pkg.go.dev/regexp/syntax>.
|
||||
|
||||
If you want to read a more comprehensive documentation about the
|
||||
topic and have perl installed you can read it with:
|
||||
|
||||
perldoc perlre
|
||||
|
||||
Or read it online: L<https://perldoc.perl.org/perlre>.
|
||||
Or read it online: L<https://perldoc.perl.org/perlre>. But please note
|
||||
that the GO regexp engine does NOT support all perl regex terms,
|
||||
especially look-ahead and look-behind.
|
||||
|
||||
A note on modifiers: the regexp engine used in tablizer uses another
|
||||
modifier syntax:
|
||||
If you want to supply flags to a regex, then surround it with slashes
|
||||
and append the flag. The following flags are supported:
|
||||
|
||||
(?MODIFIER)
|
||||
|
||||
The most important modifiers are:
|
||||
|
||||
C<i> ignore case
|
||||
C<m> multiline mode
|
||||
C<s> single line mode
|
||||
i => case insensitive
|
||||
! => negative match
|
||||
|
||||
Example for a case insensitive search:
|
||||
|
||||
kubectl get pods -A | tablizer "(?i)account"
|
||||
kubectl get pods -A | tablizer "/account/i"
|
||||
|
||||
You can use the experimental fuzzy search feature by providing the
|
||||
If you use the C<!> flag, then the regex match will be negated, that
|
||||
is, if a line in the input matches the given regex, but C<!> is
|
||||
supplied, tablizer will NOT include it in the output.
|
||||
|
||||
For example, here we want to get all lines matching "foo" but not
|
||||
"bar":
|
||||
|
||||
cat table | tablizer foo '/bar/!'
|
||||
|
||||
This would match a line "foo zorro" but not "foo bar".
|
||||
|
||||
The flags can also be combined.
|
||||
|
||||
You can also use the experimental fuzzy search feature by providing the
|
||||
option B<-z>, in which case the pattern is regarded as a fuzzy search
|
||||
term, not a regexp.
|
||||
|
||||
@@ -292,19 +302,6 @@ table and B<markdown> which prints a Markdown table, B<yaml>, which
|
||||
prints yaml encoding and CSV mode, which prints a comma separated
|
||||
value file.
|
||||
|
||||
=head2 PUT FIELDS TO CLIPBOARD
|
||||
|
||||
You can let tablizer put fields to the clipboard using the option
|
||||
C<-y>. This best fits the use-case when the result of your filtering
|
||||
yields just one row. For example:
|
||||
|
||||
cloudctl cluster ls | tablizer -yid matchbox
|
||||
|
||||
If "matchbox" matches one cluster, you can immediately use the id of
|
||||
that cluster somewhere else and paste it. Of course, if there are
|
||||
multiple matches, then all id's will be put into the clipboard
|
||||
separated by one space.
|
||||
|
||||
=head2 ENVIRONMENT VARIABLES
|
||||
|
||||
B<tablizer> supports certain environment variables which use can use
|
||||
|
||||
Reference in New Issue
Block a user