From fc0352efa9c2b489e6f5d614abb7791b091557bf Mon Sep 17 00:00:00 2001 From: "T. von Dein" Date: Mon, 8 Dec 2025 22:01:15 +0100 Subject: [PATCH] add support for template output mode with new option --templage (#47) --- cfg/config.go | 7 +++++- cmd/root.go | 4 +++- cmd/shortusage.go | 6 ++--- cmd/tablizer.go | 40 +++++++++++++++++++++---------- go.mod | 11 +++++++++ go.sum | 22 +++++++++++++++++ lib/printer.go | 26 ++++++++++++++++++++ lib/{common.go => tabdata.go} | 45 ++++++++++++++++++++++++++++++++++- t/test-template.txtar | 16 +++++++++++++ tablizer.1 | 45 +++++++++++++++++++++++------------ tablizer.pod | 42 +++++++++++++++++++++----------- 11 files changed, 216 insertions(+), 48 deletions(-) rename lib/{common.go => tabdata.go} (55%) create mode 100644 t/test-template.txtar diff --git a/cfg/config.go b/cfg/config.go index b61d988..2517263 100644 --- a/cfg/config.go +++ b/cfg/config.go @@ -28,7 +28,7 @@ import ( ) const ( - Version = "v1.5.11" + Version = "v1.5.12" MAXPARTS = 2 ) @@ -95,6 +95,7 @@ type Config struct { InputJSON bool AutoHeaders bool CustomHeaders []string + Template string SortMode string SortDescending bool @@ -142,6 +143,7 @@ type Modeflag struct { A bool C bool J bool + P bool // template } // used for switching printers @@ -154,6 +156,7 @@ const ( CSV ASCII Json + Template ) // various sort types @@ -294,6 +297,8 @@ func (conf *Config) PrepareModeFlags(flag Modeflag) { conf.OutputMode = CSV case flag.J: conf.OutputMode = Json + case conf.Template != "": + conf.OutputMode = Template default: conf.OutputMode = ASCII } diff --git a/cmd/root.go b/cmd/root.go index 6086305..d9ceddf 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -23,9 +23,9 @@ import ( "slices" "strings" - "github.com/spf13/cobra" "codeberg.org/scip/tablizer/cfg" "codeberg.org/scip/tablizer/lib" + "github.com/spf13/cobra" ) func completion(cmd *cobra.Command, mode string) error { @@ -179,6 +179,8 @@ func Execute() { "Enable ASCII output (default)") rootCmd.MarkFlagsMutuallyExclusive("extended", "markdown", "orgtbl", "shell", "yaml", "csv") + rootCmd.PersistentFlags().StringVarP(&conf.Template, "template", "P", "", + "template for template output mode") // config file rootCmd.PersistentFlags().StringVarP(&conf.Configfile, "config", "f", cfg.DefaultConfigfile, diff --git a/cmd/shortusage.go b/cmd/shortusage.go index 34cc3ee..aa2f118 100644 --- a/cmd/shortusage.go +++ b/cmd/shortusage.go @@ -13,6 +13,6 @@ const shortusage = `tablizer [regex,...] [-r file] [flags] -x col,... use custom headers -d debug -o char use char as output separator -g auto generate headers --O org -C CSV -M md -X ext -S shell -Y yaml -J json -D sort descending order --m show manual --help show detailed help -v show version --a sort by age -i sort numerically -t sort by time` +-O org -C CSV -M md -X ext -S shell -Y yaml -J json -P template +-a sort by age -i sort numerically -t sort by time -D sort descending order +-m show manual -v show version --help show detailed help` diff --git a/cmd/tablizer.go b/cmd/tablizer.go index 9a4e851..d27a71e 100644 --- a/cmd/tablizer.go +++ b/cmd/tablizer.go @@ -34,6 +34,7 @@ SYNOPSIS -J, --jsonout Enable JSON output -C, --csv Enable CSV output -A, --ascii Default output mode, ascii tabular + -P, --template Enable template mode with template -L, --hightlight-lines Use alternating background colors for tables -o, --ofs Output field separator, used by -A and -C. -y, --yank-columns Yank specified columns (separated by ,) to clipboard, @@ -77,16 +78,16 @@ DESCRIPTION use the -v option to exclude all rows which match the pattern. Hence: # read from STDIN - kubectl get pods | tablizer + > kubectl get pods | tablizer # read a file - tablizer -r filename + > tablizer -r filename # search for pattern in a file (works like grep) - tablizer regex -r filename + > tablizer regex -r filename # search for pattern in STDIN - kubectl get pods | tablizer regex + > kubectl get pods | tablizer regex The output looks like the original one. You can add the option -n, then every header field will have a numer associated with it, e.g.: @@ -96,18 +97,18 @@ DESCRIPTION These numbers denote the column and you can use them to specify which columns you want to have in your output (see COLUMNS: - kubectl get pods | tablizer -c1,3 + > kubectl get pods | tablizer -c1,3 You can specify the numbers in any order but output will always follow the original order. However, you may also just use the header names instead of numbers, eg: - kubectl get pods | tablizer -cname,status + > kubectl get pods | tablizer -cname,status You can also use regular expressions with -c, eg: - kubectl get pods | tablizer -c '[ae]' + > kubectl get pods | tablizer -c '[ae]' By default tablizer shows a header containing the names of each column. This can be disabled using the -H option. Be aware that this only @@ -218,7 +219,7 @@ DESCRIPTION Example for a case insensitive search: - kubectl get pods -A | tablizer "/account/i" + > kubectl get pods -A | tablizer "/account/i" 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, @@ -284,7 +285,7 @@ DESCRIPTION We want to see only the CMD column and use a regex for this: - ps | tablizer -s '\s+' -c C + > ps | tablizer -s '\s+' -c C CMD(4) bash ps @@ -315,7 +316,7 @@ DESCRIPTION Example: - cat t/testtable2 + > cat t/testtable2 NAME DURATION x 10 a 100 @@ -323,7 +324,7 @@ DESCRIPTION u 4 k 6 - cat t/testtable2 | tablizer -T2 -R '/^\d/4/' -n + > cat t/testtable2 | tablizer -T2 -R '/^\d/4/' -n NAME DURATION x 40 a 400 @@ -339,7 +340,7 @@ DESCRIPTION header left, value right, aligned by the field widths. Here's an example: - kubectl get pods | ./tablizer -o extended + > kubectl get pods | ./tablizer -o extended NAME: repldepl-7bcd8d5b64-7zq4l READY: 1/1 STATUS: Running @@ -352,7 +353,7 @@ DESCRIPTION The option -o shell can be used if the output has to be processed by the shell, it prints variable assignments for each cell, one line per row: - kubectl get pods | ./tablizer -o extended ./tablizer -o shell + > kubectl get pods | ./tablizer -o extended ./tablizer -o shell NAME="repldepl-7bcd8d5b64-7zq4l" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h" NAME="repldepl-7bcd8d5b64-m48n8" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h" NAME="repldepl-7bcd8d5b64-q2bf4" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h" @@ -364,6 +365,18 @@ DESCRIPTION markdown which prints a Markdown table, yaml, which prints yaml encoding and CSV mode, which prints a comma separated value file. + A special output mode ist the Template mode, activated with the option + "--template". Template language is the Golang template language: + . You can also use lot's of additional + functions from: . Here's an + example: + + > kubectl get pods | tablizer --template "{{.name}} is {{.status}}" + alertmanager-kube-prometheus-alertmanager-0 is Running + grafana-fcc54cbc9-bk7s8 is Running + + You can use header names as variables. + 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 @@ -532,6 +545,7 @@ Output Flags (mutually exclusive): -J, --jsonout Enable JSON output -C, --csv Enable CSV output -A, --ascii Default output mode, ascii tabular + -P, --template Enable template mode with template -L, --hightlight-lines Use alternating background colors for tables -o, --ofs Output field separator, used by -A and -C. -y, --yank-columns Yank specified columns (separated by ,) to clipboard, diff --git a/go.mod b/go.mod index fcc1c38..4d51db3 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,10 @@ require ( ) require ( + dario.cat/mergo v1.0.1 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect @@ -34,12 +38,16 @@ require ( github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fatih/color v1.18.0 // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect @@ -49,9 +57,12 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/spf13/cast v1.7.0 // indirect github.com/spf13/pflag v1.0.9 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/zclconf/go-cty v1.16.3 // indirect + golang.org/x/crypto v0.38.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/sync v0.15.0 // indirect diff --git a/go.sum b/go.sum index 185fca0..cc387e2 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,11 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= @@ -38,12 +46,16 @@ github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0= github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E= github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA= github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs= github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE= github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= @@ -60,8 +72,12 @@ github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRC github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= @@ -88,6 +104,10 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= @@ -107,6 +127,8 @@ github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6 github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= diff --git a/lib/printer.go b/lib/printer.go index 2aa6463..72ba39e 100644 --- a/lib/printer.go +++ b/lib/printer.go @@ -25,8 +25,10 @@ import ( "log" "strconv" "strings" + "text/template" "codeberg.org/scip/tablizer/cfg" + "github.com/Masterminds/sprig/v3" "github.com/gookit/color" "github.com/olekukonko/tablewriter" "github.com/olekukonko/tablewriter/renderer" @@ -66,6 +68,8 @@ func printData(writer io.Writer, conf cfg.Config, data *Tabdata) { printJsonData(writer, data) case cfg.CSV: printCSVData(writer, conf, data) + case cfg.Template: + printTemplateData(writer, conf, data) default: printASCIIData(writer, conf, data) } @@ -388,3 +392,25 @@ func printCSVData(writer io.Writer, conf cfg.Config, data *Tabdata) { log.Fatal(err) } } + +func printTemplateData(writer io.Writer, conf cfg.Config, data *Tabdata) { + tmpl, err := template.New("printer").Funcs(sprig.TxtFuncMap()).Parse(conf.Template) + if err != nil { + log.Fatalf("failed to parse template: %s", err) + } + + buf := strings.Builder{} + + for line, dict := range data.ToMap() { + err = tmpl.Execute(&buf, dict) + if err != nil { + log.Fatalf("failed to execute template in line %d: %s", line, err) + } + + buf.WriteString("\n") + } + + if _, err := fmt.Fprintln(writer, buf.String()); err != nil { + log.Fatalf("failed to print output: %s", err) + } +} diff --git a/lib/common.go b/lib/tabdata.go similarity index 55% rename from lib/common.go rename to lib/tabdata.go index 6a6c929..9c1af47 100644 --- a/lib/common.go +++ b/lib/tabdata.go @@ -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 it under the terms of the GNU General Public License as published by @@ -17,6 +17,11 @@ along with this program. If not, see . package lib +import ( + "strconv" + "strings" +) + // contains a whole parsed table type Tabdata struct { maxwidthHeader int // longest header @@ -34,3 +39,41 @@ func (data *Tabdata) CloneEmpty() Tabdata { return newdata } + +// convert our internal data to a list of maps +func (data *Tabdata) ToMap() []map[string]any { + dictlist := make([]map[string]any, len(data.entries)) + headers := make([]string, len(data.headers)) + + for idx, header := range data.headers { + headers[idx] = strings.ToLower(header) + } + + for line, row := range data.entries { + dict := make(map[string]any, len(data.headers)) + + for col, cell := range row { + n, ok := strconv.Atoi(cell) + if ok == nil { + dict[headers[col]] = n + continue + } + + if strings.ToLower(cell) == "true" { + dict[headers[col]] = true + continue + } + + if strings.ToLower(cell) == "false" { + dict[headers[col]] = false + continue + } + + dict[headers[col]] = cell + } + + dictlist[line] = dict + } + + return dictlist +} diff --git a/t/test-template.txtar b/t/test-template.txtar new file mode 100644 index 0000000..402f32b --- /dev/null +++ b/t/test-template.txtar @@ -0,0 +1,16 @@ +exec tablizer -r testtable.txt -P '{{.species | title}}s are {{.type }}' +stdout 'Humans are invasive' + +exec tablizer -r testtable.txt -P '{{.species | sha256sum}}' +stdout '79a5478768d2447431a90f7f4549df735f50ad541371464c248abc7522dc3a01' + +exec tablizer -r testtable.txt -P '{{add 100 .count}}' +stdout 104 + +-- testtable.txt -- +SPECIES TYPE HOME COUNT STAGE +human invasive earth 4 brink +riedl peaceful keauna 33 civilized +namak invasive namak 123 imperium +heduu peaceful iu 66 imperium +kenaha peaceful kohi 3333 hunter-gatherer diff --git a/tablizer.1 b/tablizer.1 index a6c1799..87e4c00 100644 --- a/tablizer.1 +++ b/tablizer.1 @@ -133,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "TABLIZER 1" -.TH TABLIZER 1 "2025-10-13" "1" "User Commands" +.TH TABLIZER 1 "2025-12-08" "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 @@ -172,6 +172,7 @@ tablizer \- Manipulate tabular output of other programs \& \-J, \-\-jsonout Enable JSON output \& \-C, \-\-csv Enable CSV output \& \-A, \-\-ascii Default output mode, ascii tabular +\& \-P, \-\-template Enable template mode with template \& \-L, \-\-hightlight\-lines Use alternating background colors for tables \& \-o, \-\-ofs Output field separator, used by \-A and \-C. \& \-y, \-\-yank\-columns Yank specified columns (separated by ,) to clipboard, @@ -219,16 +220,16 @@ pattern. Hence: .PP .Vb 2 \& # read from STDIN -\& kubectl get pods | tablizer +\& > kubectl get pods | tablizer \& \& # read a file -\& tablizer \-r filename +\& > tablizer \-r filename \& \& # search for pattern in a file (works like grep) -\& tablizer regex \-r filename +\& > tablizer regex \-r filename \& \& # search for pattern in STDIN -\& kubectl get pods | tablizer regex +\& > kubectl get pods | tablizer regex .Ve .PP The output looks like the original one. You can add the option \fB\-n\fR, @@ -242,7 +243,7 @@ These numbers denote the column and you can use them to specify which columns you want to have in your output (see \s-1COLUMNS\s0: .PP .Vb 1 -\& kubectl get pods | tablizer \-c1,3 +\& > kubectl get pods | tablizer \-c1,3 .Ve .PP You can specify the numbers in any order but output will always follow @@ -252,13 +253,13 @@ However, you may also just use the header names instead of numbers, eg: .PP .Vb 1 -\& kubectl get pods | tablizer \-cname,status +\& > kubectl get pods | tablizer \-cname,status .Ve .PP You can also use regular expressions with \fB\-c\fR, eg: .PP .Vb 1 -\& kubectl get pods | tablizer \-c \*(Aq[ae]\*(Aq +\& > kubectl get pods | tablizer \-c \*(Aq[ae]\*(Aq .Ve .PP By default tablizer shows a header containing the names of each @@ -381,7 +382,7 @@ and append the flag. The following flags are supported: Example for a case insensitive search: .PP .Vb 1 -\& kubectl get pods \-A | tablizer "/account/i" +\& > kubectl get pods \-A | tablizer "/account/i" .Ve .PP If you use the \f(CW\*(C`!\*(C'\fR flag, then the regex match will be negated, that @@ -460,7 +461,7 @@ Lets take this table: We want to see only the \s-1CMD\s0 column and use a regex for this: .PP .Vb 6 -\& ps | tablizer \-s \*(Aq\es+\*(Aq \-c C +\& > ps | tablizer \-s \*(Aq\es+\*(Aq \-c C \& CMD(4) \& bash \& ps @@ -497,7 +498,7 @@ use a regexp containing the \f(CW\*(C`/\*(C'\fR character, eg: Example: .PP .Vb 7 -\& cat t/testtable2 +\& > cat t/testtable2 \& NAME DURATION \& x 10 \& a 100 @@ -505,7 +506,7 @@ Example: \& u 4 \& k 6 \& -\& cat t/testtable2 | tablizer \-T2 \-R \*(Aq/^\ed/4/\*(Aq \-n +\& > cat t/testtable2 | tablizer \-T2 \-R \*(Aq/^\ed/4/\*(Aq \-n \& NAME DURATION \& x 40 \& a 400 @@ -523,7 +524,7 @@ printed vertically, header left, value right, aligned by the field widths. Here's an example: .PP .Vb 6 -\& kubectl get pods | ./tablizer \-o extended +\& > kubectl get pods | ./tablizer \-o extended \& NAME: repldepl\-7bcd8d5b64\-7zq4l \& READY: 1/1 \& STATUS: Running @@ -539,7 +540,7 @@ by the shell, it prints variable assignments for each cell, one line per row: .PP .Vb 4 -\& kubectl get pods | ./tablizer \-o extended ./tablizer \-o shell +\& > kubectl get pods | ./tablizer \-o extended ./tablizer \-o shell \& NAME="repldepl\-7bcd8d5b64\-7zq4l" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h" \& NAME="repldepl\-7bcd8d5b64\-m48n8" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h" \& NAME="repldepl\-7bcd8d5b64\-q2bf4" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h" @@ -550,8 +551,22 @@ You can use this in an eval loop. Beside normal ascii mode (the default) and extended mode there are 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 +prints yaml encoding and \fB\s-1CSV\s0\fR mode, which prints a comma separated value file. +.PP +A special output mode ist the \fBTemplate\fR mode, activated with the +option \f(CW\*(C`\-\-template\*(C'\fR. Template language is the Golang template +language: . You can also use lot's +of additional functions from: +. Here's an example: +.PP +.Vb 3 +\& > kubectl get pods | tablizer \-\-template "{{.name}} is {{.status}}" +\& alertmanager\-kube\-prometheus\-alertmanager\-0 is Running +\& grafana\-fcc54cbc9\-bk7s8 is Running +.Ve +.PP +You can use header names as variables. .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 diff --git a/tablizer.pod b/tablizer.pod index f616fec..740cdb0 100644 --- a/tablizer.pod +++ b/tablizer.pod @@ -33,6 +33,7 @@ tablizer - Manipulate tabular output of other programs -J, --jsonout Enable JSON output -C, --csv Enable CSV output -A, --ascii Default output mode, ascii tabular + -P, --template Enable template mode with template -L, --hightlight-lines Use alternating background colors for tables -o, --ofs Output field separator, used by -A and -C. -y, --yank-columns Yank specified columns (separated by ,) to clipboard, @@ -80,16 +81,16 @@ may also use the B<-v> option to exclude all rows which match the pattern. Hence: # read from STDIN - kubectl get pods | tablizer + > kubectl get pods | tablizer # read a file - tablizer -r filename + > tablizer -r filename # search for pattern in a file (works like grep) - tablizer regex -r filename + > tablizer regex -r filename # search for pattern in STDIN - kubectl get pods | tablizer regex + > kubectl get pods | tablizer regex The output looks like the original one. You can add the option B<-n>, then every header field will have a numer associated with it, e.g.: @@ -99,7 +100,7 @@ then every header field will have a numer associated with it, e.g.: These numbers denote the column and you can use them to specify which columns you want to have in your output (see L: - kubectl get pods | tablizer -c1,3 + > kubectl get pods | tablizer -c1,3 You can specify the numbers in any order but output will always follow the original order. @@ -107,11 +108,11 @@ the original order. However, you may also just use the header names instead of numbers, eg: - kubectl get pods | tablizer -cname,status + > kubectl get pods | tablizer -cname,status You can also use regular expressions with B<-c>, eg: - kubectl get pods | tablizer -c '[ae]' + > kubectl get pods | tablizer -c '[ae]' By default tablizer shows a header containing the names of each column. This can be disabled using the B<-H> option. Be aware that @@ -236,7 +237,7 @@ and append the flag. The following flags are supported: Example for a case insensitive search: - kubectl get pods -A | tablizer "/account/i" + > kubectl get pods -A | tablizer "/account/i" 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 @@ -307,7 +308,7 @@ Lets take this table: We want to see only the CMD column and use a regex for this: - ps | tablizer -s '\s+' -c C + > ps | tablizer -s '\s+' -c C CMD(4) bash ps @@ -340,7 +341,7 @@ use a regexp containing the C character, eg: Example: - cat t/testtable2 + > cat t/testtable2 NAME DURATION x 10 a 100 @@ -348,7 +349,7 @@ Example: u 4 k 6 - cat t/testtable2 | tablizer -T2 -R '/^\d/4/' -n + > cat t/testtable2 | tablizer -T2 -R '/^\d/4/' -n NAME DURATION x 40 a 400 @@ -365,7 +366,7 @@ useful which enables I. In this mode, each row will be printed vertically, header left, value right, aligned by the field widths. Here's an example: - kubectl get pods | ./tablizer -o extended + > kubectl get pods | ./tablizer -o extended NAME: repldepl-7bcd8d5b64-7zq4l READY: 1/1 STATUS: Running @@ -379,7 +380,7 @@ The option B<-o shell> can be used if the output has to be processed by the shell, it prints variable assignments for each cell, one line per row: - kubectl get pods | ./tablizer -o extended ./tablizer -o shell + > kubectl get pods | ./tablizer -o extended ./tablizer -o shell NAME="repldepl-7bcd8d5b64-7zq4l" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h" NAME="repldepl-7bcd8d5b64-m48n8" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h" NAME="repldepl-7bcd8d5b64-q2bf4" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h" @@ -389,9 +390,22 @@ You can use this in an eval loop. Beside normal ascii mode (the default) and extended mode there are more output modes available: B which prints an Emacs org-mode table and B which prints a Markdown table, B, which -prints yaml encoding and CSV mode, which prints a comma separated +prints yaml encoding and B mode, which prints a comma separated value file. +A special output mode ist the B