Compare commits

...

36 Commits

Author SHA1 Message Date
b72a99748f upd Changelog, bump version 2023-01-23 12:38:59 +01:00
3cf9310ef7 -D could not be used together with -a 2023-01-20 13:37:06 +01:00
ceae80c91c fix invalid arg handling (io, stdin) and add tests for this 2023-01-09 12:54:45 +01:00
54add2c801 only upd patch version if any 2022-11-04 20:27:22 +01:00
2d157bf2c0 force uniseg release version, see actions#3396457307/ 2022-11-04 20:22:51 +01:00
6f71a028f0 added licenses 2022-11-04 20:22:40 +01:00
dfc7c2e03e upd dep licenses, upd go modules 2022-11-04 20:10:54 +01:00
c443914222 fix spacing mess 2022-11-03 20:17:02 +01:00
eddd4e4180 add feature request template 2022-11-03 20:12:26 +01:00
0d05505493 Merge branch 'main' into development 2022-11-03 20:09:10 +01:00
T.v.Dein
a461dba10d Merge pull request #6 from TLINDEN/issue-template
Update issue templates
2022-11-03 20:08:42 +01:00
T.v.Dein
ca71f8a572 Update issue templates 2022-11-03 19:59:19 +01:00
60230eb1f6 added show-version target 2022-11-03 19:51:42 +01:00
315e8d5363 fix changelog 2022-11-03 19:30:55 +01:00
88d078a535 fix to be able to run 'make' on systems w/o perl 2022-11-03 19:26:57 +01:00
74ab3a1804 version bump 2022-11-03 13:16:02 +01:00
2d8614fa0f demo gif 2022-11-03 13:11:09 +01:00
c8bad4df1a check completion errors 2022-11-02 14:33:48 +01:00
335b2665f2 turned completion subcommand into option 2022-11-01 11:40:36 +01:00
8552270a68 added completion support 2022-10-31 16:19:12 +01:00
6f49b76607 added demo generator 2022-10-27 19:24:22 +02:00
4653eaca09 added demo generator 2022-10-27 19:23:48 +02:00
722eea7e7b add asciicast demo 2022-10-27 19:22:36 +02:00
304f2182ac js doesnt work, try image 2022-10-27 19:18:09 +02:00
73908b1661 added ascii cast 2022-10-27 19:16:41 +02:00
105ba96757 -A was not implemented, oops! 2022-10-27 18:38:46 +02:00
0681f67bc6 fix release link 2022-10-26 12:38:08 +02:00
066ddd0d98 re-organized pattern matching code 2022-10-25 18:34:28 +02:00
417faf3ff2 fixed #5, colorization now always works as expected 2022-10-25 14:24:05 +02:00
001021dac8 Workaround for issue#3: text containing tag like content is not colorized properly. 2022-10-24 17:55:31 +02:00
5c42f7ab9a check pattern on startup 2022-10-24 14:49:23 +02:00
5e65726cb0 cleanup 2022-10-24 14:05:50 +02:00
138ae51936 added CSV output mode, enhanced parser tests 2022-10-23 16:57:30 +02:00
b5c802403b added CSV parsing support, enabled with -s 2022-10-22 12:27:33 +02:00
e54435c2e4 added support for environment variables 2022-10-22 10:21:39 +02:00
975510c86a using enum modeflags, use my own usage template, generated from manpage so I don't have to maintain it twice, it's also nicer 2022-10-21 10:21:07 +02:00
31 changed files with 1227 additions and 338 deletions

31
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,31 @@
---
name: Bug report
about: Create a report to help us improve
title: "[bug-report]"
labels: bug
assignees: TLINDEN
---
**Describtion**
<!-- Please provide a clear and concise description of the issue: -->
**Steps To Reproduce**
<!-- Please detail the steps to reproduce the behavior: -->
**Expected behavior**
<!-- What do you expected to happen instead? -->
**Version information**
<!--
Please provide as much version information as possible:
- if you have just installed a binary, provide the output of: tablizer --version
- if you installed from source, provide the output of: make show-version
- provide additional details: operating system and version and shell environment
-->
**Additional informations**

View File

@@ -0,0 +1,23 @@
---
name: Feature request
about: Suggest a feature
title: "[feature-request]"
labels: feature-request
assignees: TLINDEN
---
**Describtion**
<!-- Please provide a clear and concise description of the feature you desire: -->
**Version information**
<!--
Just in case the feature is already present, please provide as
much version information as possible:
- if you have just installed a binary, provide the output of: tablizer --version
- if you installed from source, provide the output of: make show-version
- provide additional details: operating system and version and shell environment
-->

View File

@@ -4,6 +4,79 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org). The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org).
## [v1.0.14](https://github.com/TLINDEN/tablizer/tree/v1.0.14) - 2023-01-23
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.13...v1.0.14)
### Fixed
- The -D parameter could not be used together with -a.
- Fixed invalid argv handling: when the user wanted to read from stdin
but gave an argument which was meant as a pattern, but also existed
as a filename, then tablizer opened the file, ignored stdin.
- Makefile indentation
### Added
- added licens notes about dependencies
- using hard coded uniseq version, see actions#3396457307
- updated dependencies (go module versions)
## [v1.0.13](https://github.com/TLINDEN/tablizer/tree/v1.0.13) - 2022-11-03
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.12...v1.0.13)
### Added
- Added command line flag to generate shell completion code
- Added an animated demo gif to the README to demonstrate the tool
### Fixed
- The `-A` flag wasn't implemented (default output mode).
- Fixed building from source on systems w/o perls pod tools,
which is not requrired anyway since I always commit the latest
manpage.
## [v1.0.12](https://github.com/TLINDEN/tablizer/tree/v1.0.12) - 2022-10-25
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.11...v1.0.12)
### Added
- Added support to parse CSV input
- Added CSV output support
- Added support for environment variables
### Changed
- We do not use the generated help message anymore, instead we use the
usage from the manpage, which we have to maintain anyway. It looks
better and has flag groups, which cobra is still lacking as of this
writing.
- More refactoring and re-organization, runtime configuration now
lives in the cfg module.
### Fixed
- Fixed [Bug #5](https://github.com/TLINDEN/tablizer/issues/5), where
matches have not been highlighted correctly in some rare cases.
## [v1.0.11](https://github.com/TLINDEN/tablizer/tree/v1.0.11) - 2022-10-19 ## [v1.0.11](https://github.com/TLINDEN/tablizer/tree/v1.0.11) - 2022-10-19
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.10...v1.0.11) [Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.10...v1.0.11)

View File

@@ -26,21 +26,29 @@ GID = 0
BRANCH = $(shell git branch --show-current) BRANCH = $(shell git branch --show-current)
COMMIT = $(shell git rev-parse --short=8 HEAD) COMMIT = $(shell git rev-parse --short=8 HEAD)
BUILD = $(shell date +%Y.%m.%d.%H%M%S) BUILD = $(shell date +%Y.%m.%d.%H%M%S)
VERSION:= $(if $(filter $(BRANCH), development),$(version)-$(BRANCH)-$(COMMIT)-$(BUILD),$(version)) VERSION := $(if $(filter $(BRANCH), development),$(version)-$(BRANCH)-$(COMMIT)-$(BUILD),$(version))
HAVE_POD := $(shell pod2text -h 2>/dev/null)
all: $(tool).1 cmd/$(tool).go buildlocal all: $(tool).1 cmd/$(tool).go buildlocal
%.1: %.pod %.1: %.pod
ifdef HAVE_POD
pod2man -c "User Commands" -r 1 -s 1 $*.pod > $*.1 pod2man -c "User Commands" -r 1 -s 1 $*.pod > $*.1
endif
cmd/%.go: %.pod cmd/%.go: %.pod
ifdef HAVE_POD
echo "package cmd" > cmd/$*.go echo "package cmd" > cmd/$*.go
echo >> cmd/$*.go echo >> cmd/$*.go
echo "var manpage = \`" >> cmd/$*.go echo "var manpage = \`" >> cmd/$*.go
pod2text $*.pod >> cmd/$*.go pod2text $*.pod >> cmd/$*.go
echo "\`" >> cmd/$*.go echo "\`" >> cmd/$*.go
echo "var usage = \`" >> cmd/$*.go
awk '/SYNOPS/{f=1;next} /DESCR/{f=0} f' $*.pod | sed 's/^ //' >> cmd/$*.go
echo "\`" >> cmd/$*.go
endif
buildlocal: buildlocal:
go build -ldflags "-X 'github.com/tlinden/tablizer/cfg.VERSION=$(VERSION)'" go build -ldflags "-X 'github.com/tlinden/tablizer/cfg.VERSION=$(VERSION)'"
@@ -59,6 +67,7 @@ clean:
test: test:
go test -v ./... go test -v ./...
bash t/test.sh
singletest: singletest:
@echo "Call like this: ''make singletest TEST=TestPrepareColumns MOD=lib" @echo "Call like this: ''make singletest TEST=TestPrepareColumns MOD=lib"
@@ -67,3 +76,18 @@ singletest:
cover-report: cover-report:
go test ./... -cover -coverprofile=coverage.out go test ./... -cover -coverprofile=coverage.out
go tool cover -html=coverage.out go tool cover -html=coverage.out
show-versions: buildlocal
@echo "### tablizer version:"
@./tablizer --version
@echo
@echo "### go module versions:"
@go list -m all
@echo
@echo "### go version used for building:"
@grep -m 1 go go.mod
goupdate:
go get -t -u=patch ./...

View File

@@ -72,11 +72,15 @@ repldepl-7bcd8d5b64-q2bf4 1/1 Running 1 (69m ago) 5h26m
There are more output modes like org-mode (orgtbl) and markdown. There are more output modes like org-mode (orgtbl) and markdown.
## Demo
[![asciicast](demo/tablizer-demo.gif)](https://asciinema.org/a/9FKc3HPnlg8D2X8otheleEa9t)
## Installation ## Installation
There are multiple ways to install **tablizer**: There are multiple ways to install **tablizer**:
- Go to the [latest release page](https://github.com/muesli/mango/releases/latest), - Go to the [latest release page](https://github.com/tlinden/tablizer/releases/latest),
locate the binary for your operating system and platform. locate the binary for your operating system and platform.
Download it and put it into some directory within your `$PATH` variable. Download it and put it into some directory within your `$PATH` variable.

View File

@@ -1,15 +1,9 @@
## Fixes to be implemented ## Fixes to be implemented
- rm printYamlData() log.Fatal(), maybe return error on all printers?
- printShellData() checks Columns unnecessarily (compare to yaml or extended)
## Features to be implemented ## Features to be implemented
- add output mode csv - add comment support (csf.NewReader().Comment = '#')
- add --no-headers option - add --no-headers option
- add input parsing support for CSV including unquoting of stuff
like: `"xxx","1919 b"` etc, maybe an extra option for unquoting

View File

@@ -20,12 +20,12 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/gookit/color" "github.com/gookit/color"
"os"
"regexp" "regexp"
) )
const DefaultSeparator string = `(\s\s+|\t)` const DefaultSeparator string = `(\s\s+|\t)`
const ValidOutputModes string = "(orgtbl|markdown|extended|ascii|yaml|shell)" const Version string = "v1.0.14"
const Version string = "v1.0.11"
var VERSION string // maintained by -x var VERSION string // maintained by -x
@@ -35,9 +35,10 @@ type Config struct {
Columns string Columns string
UseColumns []int UseColumns []int
Separator string Separator string
OutputMode string OutputMode int
InvertMatch bool InvertMatch bool
Pattern string Pattern string
PatternR *regexp.Regexp
SortMode string SortMode string
SortDescending bool SortDescending bool
@@ -45,12 +46,10 @@ type Config struct {
/* /*
FIXME: make configurable somehow, config file or ENV FIXME: make configurable somehow, config file or ENV
see https://github.com/gookit/color will be set by see https://github.com/gookit/color.
io.ProcessFiles() according to currently supported
color mode.
*/ */
MatchFG string ColorStyle color.Style
MatchBG string
NoColor bool NoColor bool
} }
@@ -62,8 +61,20 @@ type Modeflag struct {
S bool S bool
Y bool Y bool
A bool A bool
C bool
} }
// used for switching printers
const (
Extended = iota + 1
Orgtbl
Markdown
Shell
Yaml
CSV
Ascii
)
// various sort types // various sort types
type Sortmode struct { type Sortmode struct {
Numeric bool Numeric bool
@@ -71,22 +82,43 @@ type Sortmode struct {
Age bool Age bool
} }
func Colors() map[color.Level]map[string]string { // default color schemes
// default color schemes func Colors() map[color.Level]map[string]color.Color {
return map[color.Level]map[string]string{ return map[color.Level]map[string]color.Color{
color.Level16: { color.Level16: {
"bg": "green", "fg": "black", "bg": color.BgGreen, "fg": color.FgBlack,
}, },
color.Level256: { color.Level256: {
"bg": "lightGreen", "fg": "black", "bg": color.BgLightGreen, "fg": color.FgBlack,
}, },
color.LevelRgb: { color.LevelRgb: {
// FIXME: maybe use something nicer // FIXME: maybe use something nicer
"bg": "lightGreen", "fg": "black", "bg": color.BgLightGreen, "fg": color.FgBlack,
}, },
} }
} }
// find supported color mode, modifies config based on constants
func (c *Config) DetermineColormode() {
if !isTerminal(os.Stdout) {
color.Disable()
} else {
level := color.TermColorLevel()
colors := Colors()
c.ColorStyle = color.New(colors[level]["bg"], colors[level]["fg"])
}
}
// Return true if current terminal is interactive
func isTerminal(f *os.File) bool {
o, _ := f.Stat()
if (o.Mode() & os.ModeCharDevice) == os.ModeCharDevice {
return true
} else {
return false
}
}
func Getversion() string { func Getversion() string {
// main program version // main program version
@@ -97,39 +129,6 @@ func Getversion() string {
return fmt.Sprintf("This is tablizer version %s", VERSION) return fmt.Sprintf("This is tablizer version %s", VERSION)
} }
func (conf *Config) PrepareModeFlags(flag Modeflag, mode string) error {
if len(mode) == 0 {
// associate short flags like -X with mode selector
switch {
case flag.X:
conf.OutputMode = "extended"
case flag.M:
conf.OutputMode = "markdown"
case flag.O:
conf.OutputMode = "orgtbl"
case flag.S:
conf.OutputMode = "shell"
conf.NoNumbering = true
case flag.Y:
conf.OutputMode = "yaml"
conf.NoNumbering = true
default:
conf.OutputMode = "ascii"
}
} else {
r, _ := regexp.Compile(ValidOutputModes) // hardcoded, no fail expected
match := r.MatchString(mode)
if !match {
return errors.New("Invalid output mode!")
}
conf.OutputMode = mode
}
return nil
}
func (conf *Config) PrepareSortFlags(flag Sortmode) { func (conf *Config) PrepareSortFlags(flag Sortmode) {
switch { switch {
case flag.Numeric: case flag.Numeric:
@@ -142,3 +141,60 @@ func (conf *Config) PrepareSortFlags(flag Sortmode) {
conf.SortMode = "string" conf.SortMode = "string"
} }
} }
func (conf *Config) PrepareModeFlags(flag Modeflag) {
switch {
case flag.X:
conf.OutputMode = Extended
case flag.O:
conf.OutputMode = Orgtbl
case flag.M:
conf.OutputMode = Markdown
case flag.S:
conf.OutputMode = Shell
case flag.Y:
conf.OutputMode = Yaml
case flag.C:
conf.OutputMode = CSV
default:
conf.OutputMode = Ascii
}
}
func (c *Config) CheckEnv() {
// check for environment vars, command line flags have precedence,
// NO_COLOR is being checked by the color module itself.
if !c.NoNumbering {
_, set := os.LookupEnv("T_NO_HEADER_NUMBERING")
if set {
c.NoNumbering = true
}
}
if len(c.Columns) == 0 {
cols := os.Getenv("T_COLUMNS")
if len(cols) > 1 {
c.Columns = cols
}
}
}
func (c *Config) ApplyDefaults() {
// mode specific defaults
if c.OutputMode == Yaml || c.OutputMode == CSV {
c.NoNumbering = true
}
}
func (c *Config) PreparePattern(pattern string) error {
PatternR, err := regexp.Compile(pattern)
if err != nil {
return errors.Unwrap(fmt.Errorf("Regexp pattern %s is invalid: %w", c.Pattern, err))
}
c.PatternR = PatternR
c.Pattern = pattern
return nil
}

View File

@@ -26,46 +26,26 @@ import (
func TestPrepareModeFlags(t *testing.T) { func TestPrepareModeFlags(t *testing.T) {
var tests = []struct { var tests = []struct {
flag Modeflag flag Modeflag
mode string // input, if any expect int // output (constant enum)
expect string // output
want bool
}{ }{
// short commandline flags like -M // short commandline flags like -M
{Modeflag{X: true}, "", "extended", false}, {Modeflag{X: true}, Extended},
{Modeflag{S: true}, "", "shell", false}, {Modeflag{S: true}, Shell},
{Modeflag{O: true}, "", "orgtbl", false}, {Modeflag{O: true}, Orgtbl},
{Modeflag{Y: true}, "", "yaml", false}, {Modeflag{Y: true}, Yaml},
{Modeflag{M: true}, "", "markdown", false}, {Modeflag{M: true}, Markdown},
{Modeflag{}, "", "ascii", false}, {Modeflag{}, Ascii},
// long flags like -o yaml
{Modeflag{}, "extended", "extended", false},
{Modeflag{}, "shell", "shell", false},
{Modeflag{}, "orgtbl", "orgtbl", false},
{Modeflag{}, "yaml", "yaml", false},
{Modeflag{}, "markdown", "markdown", false},
// failing
{Modeflag{}, "blah", "", true},
} }
// FIXME: use a map for easier printing
for _, tt := range tests { for _, tt := range tests {
testname := fmt.Sprintf("PrepareModeFlags-flags-mode-%s-expect-%s-want-%t", testname := fmt.Sprintf("PrepareModeFlags-expect-%d", tt.expect)
tt.mode, tt.expect, tt.want)
t.Run(testname, func(t *testing.T) { t.Run(testname, func(t *testing.T) {
c := Config{OutputMode: tt.mode} c := Config{}
// check either flag or pre filled mode, whatever is defined in tt c.PrepareModeFlags(tt.flag)
err := c.PrepareModeFlags(tt.flag, tt.mode)
if err != nil {
if !tt.want {
// expect to fail
t.Fatalf("PrepareModeFlags returned unexpected error: %s", err)
}
} else {
if c.OutputMode != tt.expect { if c.OutputMode != tt.expect {
t.Errorf("got: %s, expect: %s", c.OutputMode, tt.expect) t.Errorf("got: %d, expect: %d", c.OutputMode, tt.expect)
}
} }
}) })
} }
@@ -96,3 +76,28 @@ func TestPrepareSortFlags(t *testing.T) {
}) })
} }
} }
func TestPreparePattern(t *testing.T) {
var tests = []struct {
pattern string
wanterr bool
}{
{"[A-Z]+", false},
{"[a-z", true},
}
for _, tt := range tests {
testname := fmt.Sprintf("PreparePattern-pattern-%s-wanterr-%t", tt.pattern, tt.wanterr)
t.Run(testname, func(t *testing.T) {
c := Config{}
err := c.PreparePattern(tt.pattern)
if err != nil {
if !tt.wanterr {
t.Errorf("PreparePattern returned error: %s", err)
}
}
})
}
}

View File

@@ -18,6 +18,7 @@ package cmd
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/tlinden/tablizer/cfg" "github.com/tlinden/tablizer/cfg"
@@ -25,6 +26,7 @@ import (
"log" "log"
"os" "os"
"os/exec" "os/exec"
"strings"
) )
func man() { func man() {
@@ -44,12 +46,27 @@ func man() {
} }
} }
func completion(cmd *cobra.Command, mode string) error {
switch mode {
case "bash":
return cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
return cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
return cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
default:
return errors.New("Invalid shell parameter! Valid ones: bash|zsh|fish|powershell")
}
}
func Execute() { func Execute() {
var ( var (
conf cfg.Config conf cfg.Config
ShowManual bool ShowManual bool
Outputmode string
ShowVersion bool ShowVersion bool
ShowCompletion string
modeflag cfg.Modeflag modeflag cfg.Modeflag
sortmode cfg.Sortmode sortmode cfg.Sortmode
) )
@@ -69,16 +86,19 @@ func Execute() {
return nil return nil
} }
// prepare flags if len(ShowCompletion) > 0 {
err := conf.PrepareModeFlags(modeflag, Outputmode) return completion(cmd, ShowCompletion)
if err != nil {
return err
} }
// Setup
conf.CheckEnv()
conf.PrepareModeFlags(modeflag)
conf.PrepareSortFlags(sortmode) conf.PrepareSortFlags(sortmode)
conf.DetermineColormode()
conf.ApplyDefaults()
// actual execution starts here // actual execution starts here
return lib.ProcessFiles(conf, args) return lib.ProcessFiles(&conf, args)
}, },
} }
@@ -89,31 +109,31 @@ func Execute() {
rootCmd.PersistentFlags().BoolVarP(&ShowVersion, "version", "V", false, "Print program version") rootCmd.PersistentFlags().BoolVarP(&ShowVersion, "version", "V", false, "Print program version")
rootCmd.PersistentFlags().BoolVarP(&conf.InvertMatch, "invert-match", "v", false, "select non-matching rows") rootCmd.PersistentFlags().BoolVarP(&conf.InvertMatch, "invert-match", "v", false, "select non-matching rows")
rootCmd.PersistentFlags().BoolVarP(&ShowManual, "man", "m", false, "Display manual page") rootCmd.PersistentFlags().BoolVarP(&ShowManual, "man", "m", false, "Display manual page")
rootCmd.PersistentFlags().StringVarP(&ShowCompletion, "completion", "", "", "Display completion code")
rootCmd.PersistentFlags().StringVarP(&conf.Separator, "separator", "s", cfg.DefaultSeparator, "Custom field separator") rootCmd.PersistentFlags().StringVarP(&conf.Separator, "separator", "s", cfg.DefaultSeparator, "Custom field separator")
rootCmd.PersistentFlags().StringVarP(&conf.Columns, "columns", "c", "", "Only show the speficied columns (separated by ,)") rootCmd.PersistentFlags().StringVarP(&conf.Columns, "columns", "c", "", "Only show the speficied columns (separated by ,)")
// sort options // sort options
rootCmd.PersistentFlags().IntVarP(&conf.SortByColumn, "sort-by", "k", 0, "Sort by column (default: 1)") rootCmd.PersistentFlags().IntVarP(&conf.SortByColumn, "sort-by", "k", 0, "Sort by column (default: 1)")
// sort mode, only 1 allowed
rootCmd.PersistentFlags().BoolVarP(&conf.SortDescending, "sort-desc", "D", false, "Sort in descending order (default: ascending)") rootCmd.PersistentFlags().BoolVarP(&conf.SortDescending, "sort-desc", "D", false, "Sort in descending order (default: ascending)")
rootCmd.PersistentFlags().BoolVarP(&sortmode.Numeric, "sort-numeric", "i", false, "sort according to string numerical value") rootCmd.PersistentFlags().BoolVarP(&sortmode.Numeric, "sort-numeric", "i", false, "sort according to string numerical value")
rootCmd.PersistentFlags().BoolVarP(&sortmode.Time, "sort-time", "t", false, "sort according to time string") rootCmd.PersistentFlags().BoolVarP(&sortmode.Time, "sort-time", "t", false, "sort according to time string")
rootCmd.PersistentFlags().BoolVarP(&sortmode.Age, "sort-age", "a", false, "sort according to age (duration) string") rootCmd.PersistentFlags().BoolVarP(&sortmode.Age, "sort-age", "a", false, "sort according to age (duration) string")
rootCmd.MarkFlagsMutuallyExclusive("sort-numeric", "sort-time", "sort-age")
// output flags, only 1 allowed, hidden, since just short cuts // output flags, only 1 allowed
rootCmd.PersistentFlags().BoolVarP(&modeflag.X, "extended", "X", false, "Enable extended output") rootCmd.PersistentFlags().BoolVarP(&modeflag.X, "extended", "X", false, "Enable extended output")
rootCmd.PersistentFlags().BoolVarP(&modeflag.M, "markdown", "M", false, "Enable markdown table output") rootCmd.PersistentFlags().BoolVarP(&modeflag.M, "markdown", "M", false, "Enable markdown table output")
rootCmd.PersistentFlags().BoolVarP(&modeflag.O, "orgtbl", "O", false, "Enable org-mode table output") rootCmd.PersistentFlags().BoolVarP(&modeflag.O, "orgtbl", "O", false, "Enable org-mode table output")
rootCmd.PersistentFlags().BoolVarP(&modeflag.S, "shell", "S", false, "Enable shell mode output") rootCmd.PersistentFlags().BoolVarP(&modeflag.S, "shell", "S", false, "Enable shell mode output")
rootCmd.PersistentFlags().BoolVarP(&modeflag.Y, "yaml", "Y", false, "Enable yaml output") rootCmd.PersistentFlags().BoolVarP(&modeflag.Y, "yaml", "Y", false, "Enable yaml output")
rootCmd.MarkFlagsMutuallyExclusive("extended", "markdown", "orgtbl", "shell", "yaml") rootCmd.PersistentFlags().BoolVarP(&modeflag.C, "csv", "C", false, "Enable CSV output")
_ = rootCmd.Flags().MarkHidden("extended") rootCmd.PersistentFlags().BoolVarP(&modeflag.A, "ascii", "A", false, "Enable ASCII output (default)")
_ = rootCmd.Flags().MarkHidden("orgtbl") rootCmd.MarkFlagsMutuallyExclusive("extended", "markdown", "orgtbl", "shell", "yaml", "csv")
_ = rootCmd.Flags().MarkHidden("markdown")
_ = rootCmd.Flags().MarkHidden("shell")
_ = rootCmd.Flags().MarkHidden("yaml")
// same thing but more common, takes precedence over above group rootCmd.SetUsageTemplate(strings.TrimSpace(usage) + "\n")
rootCmd.PersistentFlags().StringVarP(&Outputmode, "output", "o", "", "Output mode - one of: orgtbl, markdown, extended, shell, ascii(default)")
err := rootCmd.Execute() err := rootCmd.Execute()
if err != nil { if err != nil {

View File

@@ -8,24 +8,34 @@ SYNOPSIS
Usage: Usage:
tablizer [regex] [file, ...] [flags] tablizer [regex] [file, ...] [flags]
Flags: Operational Flags:
-c, --columns string Only show the speficied columns (separated by ,) -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 -v, --invert-match select non-matching rows
-m, --man Display manual page
-n, --no-numbering Disable header numbering -n, --no-numbering Disable header numbering
-N, --no-color Disable pattern highlighting -N, --no-color Disable pattern highlighting
-o, --output string Output mode - one of: orgtbl, markdown, extended, yaml, ascii(default) -s, --separator string Custom field separator
-k, --sort-by int Sort by column (default: 1)
Output Flags (mutually exclusive):
-X, --extended Enable extended output -X, --extended Enable extended output
-M, --markdown Enable markdown table output -M, --markdown Enable markdown table output
-O, --orgtbl Enable org-mode table output -O, --orgtbl Enable org-mode table output
-s, --separator string Custom field separator -S, --shell Enable shell evaluable ouput
-Y, --yaml Enable yaml output
-C, --csv Enable CSV output
-A, --ascii Default output mode, ascii tabular
Sort Mode Flags (mutually exclusive):
-a, --sort-age sort according to age (duration) string -a, --sort-age sort according to age (duration) string
-k, --sort-by int Sort by column (default: 1)
-D, --sort-desc Sort in descending order (default: ascending) -D, --sort-desc Sort in descending order (default: ascending)
-i, --sort-numeric sort according to string numerical value -i, --sort-numeric sort according to string numerical value
-t, --sort-time sort according to time string -t, --sort-time sort according to time string
Other Flags:
--completion <shell> Generate the autocompletion script for <shell>
-d, --debug Enable debugging
-h, --help help for tablizer
-m, --man Display manual page
-v, --version Print program version -v, --version Print program version
DESCRIPTION DESCRIPTION
@@ -178,8 +188,63 @@ DESCRIPTION
Beside normal ascii mode (the default) and extended mode there are more Beside normal ascii mode (the default) and extended mode there are more
output modes available: orgtbl which prints an Emacs org-mode table and output modes available: orgtbl which prints an Emacs org-mode table and
markdown which prints a Markdown table and yaml, which prints yaml markdown which prints a Markdown table, yaml, which prints yaml encoding
encoding. and CSV mode, which prints a comma separated value file.
ENVIRONMENT VARIABLES
tablizer supports certain environment variables which use can use to
influence program behavior. Commandline flags have always precedence
over environment variables.
<T_NO_HEADER_NUMBERING> - disable numbering of header fields, like -n.
<T_COLUMNS> - comma separated list of columns to output, like -c
<NO_COLORS> - disable colorization of matches, like -N
COMPLETION
Shell completion for command line options can be enabled by using the
--completion flag. The required parameter is the name of your shell.
Currently supported are: bash, zsh, fish and powershell.
Detailed instructions:
Bash:
source <(tablizer --completion bash)
To load completions for each session, execute once:
# Linux:
$ tablizer --completion bash > /etc/bash_completion.d/tablizer
# macOS:
$ tablizer --completion bash > $(brew --prefix)/etc/bash_completion.d/tablizer
Zsh:
If shell completion is not already enabled in your environment, you
will need to enable it. You can execute the following once:
echo "autoload -U compinit; compinit" >> ~/.zshrc
To load completions for each session, execute once:
$ tablizer --completion zsh > "${fpath[1]}/_tablizer"
You will need to start a new shell for this setup to take effect.
fish:
tablizer --completion fish | source
To load completions for each session, execute once:
tablizer --completion fish > ~/.config/fish/completions/tablizer.fish
PowerShell:
tablizer --completion powershell | Out-String | Invoke-Expression
To load completions for every new session, run:
tablizer --completion powershell > tablizer.ps1
and source this file from your PowerShell profile.
BUGS BUGS
In order to report a bug, unexpected behavior, feature requests or to In order to report a bug, unexpected behavior, feature requests or to
@@ -190,9 +255,9 @@ LICENSE
This software is licensed under the GNU GENERAL PUBLIC LICENSE version This software is licensed under the GNU GENERAL PUBLIC LICENSE version
3. 3.
Copyright (c) 2022 by Thomas von Dein Copyright (c) 2023 by Thomas von Dein
This software uses the following GO libraries: This software uses the following GO modules:
repr (https://github.com/alecthomas/repr) repr (https://github.com/alecthomas/repr)
Released under the MIT License, Copyright (c) 2016 Alec Thomas Released under the MIT License, Copyright (c) 2016 Alec Thomas
@@ -201,7 +266,57 @@ LICENSE
Released under the Apache 2.0 license, Copyright 2013-2022 The Cobra Released under the Apache 2.0 license, Copyright 2013-2022 The Cobra
Authors Authors
dateparse (github.com/araddon/dateparse)
Released under the MIT License, Copyright (c) 2015-2017 Aaron Raddon
color (github.com/gookit/color)
Released under the MIT License, Copyright (c) 2016 inhere
tablewriter (github.com/olekukonko/tablewriter)
Released under the MIT License, Copyright (c) 201 by Oleku Konko
yaml (gopkg.in/yaml.v3)
Released under the MIT License, Copyright (c) 2006-2011 Kirill
Simonov
AUTHORS AUTHORS
Thomas von Dein tom AT vondein DOT org Thomas von Dein tom AT vondein DOT org
` `
var usage = `
Usage:
tablizer [regex] [file, ...] [flags]
Operational Flags:
-c, --columns string Only show the speficied columns (separated by ,)
-v, --invert-match select non-matching rows
-n, --no-numbering Disable header numbering
-N, --no-color Disable pattern highlighting
-s, --separator string Custom field separator
-k, --sort-by int Sort by column (default: 1)
Output Flags (mutually exclusive):
-X, --extended Enable extended output
-M, --markdown Enable markdown table output
-O, --orgtbl Enable org-mode table output
-S, --shell Enable shell evaluable ouput
-Y, --yaml Enable yaml output
-C, --csv Enable CSV output
-A, --ascii Default output mode, ascii tabular
Sort Mode Flags (mutually exclusive):
-a, --sort-age sort according to age (duration) string
-D, --sort-desc Sort in descending order (default: ascending)
-i, --sort-numeric sort according to string numerical value
-t, --sort-time sort according to time string
Other Flags:
--completion <shell> Generate the autocompletion script for <shell>
-d, --debug Enable debugging
-h, --help help for tablizer
-m, --man Display manual page
-v, --version Print program version
`

3
demo/Makefile Normal file
View File

@@ -0,0 +1,3 @@
all:
LC_ALL=en_US.UTF-8 asciinema rec --cols 50 --row 30 -c ./demo.sh --overwrite tmp.cast
agg tmp.cast tmp.gif

31
demo/demo.sh Executable file
View File

@@ -0,0 +1,31 @@
#!/bin/bash
prompt() {
if test -n "$1"; then
echo
echo -n "% $*"
sleep 1
echo
$*
echo
echo -n "% "
else
echo -n "% "
fi
}
PATH=..:$PATH
clear
while IFS=$'\t' read -r flags table msg source _; do
echo "#"
echo "# source tabular data:"
cat $table
echo
echo "#"
echo "# $msg:"
prompt "tablizer $flags $table"
sleep 4
clear
done < <(yq -r tables.yaml \
| yq -r '.tables[] | [.flags, .table, .msg, .source] | @tsv')

4
demo/table.demo1 Normal file
View File

@@ -0,0 +1,4 @@
NAME DURATION COUNT WHEN
beta 1d10h5m1s 33 3/1/2014
alpha 4h35m 170 2013-Feb-03
ceta 33d12h 9 06/Jan/2008 15:04:05 -0700

3
demo/table.demo2 Normal file
View File

@@ -0,0 +1,3 @@
PID TTY TIME CMD
30912 pts/0 00:00:00 bash
49526 pts/0 00:00:00 ps

54
demo/tables.yaml Normal file
View File

@@ -0,0 +1,54 @@
tables:
# OUTPUTS
- flags: -A
table: table.demo1
msg: default output mode
- flags: -O
table: table.demo1
msg: orgmode output mode
- flags: -M
table: table.demo1
msg: markdown output mode
- flags: -S
table: table.demo1
msg: shell output mode
- flags: -X
table: table.demo1
msg: extended output mode
- flags: -Y
table: table.demo1
msg: yaml output mode
- flags: -C
table: table.demo1
msg: CSV output mode
# SORTS
- flags: -A -k 3
table: table.demo1
msg: sort by column 3
- flags: -A -k 4 -t
table: table.demo1
msg: sort by column 4 and sort type time
- flags: -A -k 2 -a
table: table.demo1
msg: sort by column 2 and sort type duration
# REDUCE
- flags: -A -c 1,3
table: table.demo1
msg: only display column 1 and 3
- flags: -A -c AM,RA
table: table.demo1
msg: only display columns matching /(RA|AM)/
- flags: -X -c 1,3
table: table.demo1
msg: only display column 1 and 3 in extended mode
# SEARCH
- flags: /20 -A
table: table.demo1
msg: only show rows matching /20
- flags: /20 -A -v
table: table.demo1
msg: only show rows NOT matching /20

119
demo/tablizer-demo.cast Normal file
View File

@@ -0,0 +1,119 @@
{"version": 2, "width": 80, "height": 25, "timestamp": 1666890777, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[0.004618, "o", "\u001b[H\u001b[2J\u001b[3J"]
[0.010297, "o", "#\r\n# source tabular data:\r\n"]
[0.010898, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[0.011125, "o", "\r\n#\r\n"]
[0.011177, "o", "# default output mode:\r\n"]
[0.011219, "o", "\r\n% tablizer -A table.demo1"]
[1.011851, "o", "\r\n"]
[1.013635, "o", "NAME(1)\tDURATION(2)\tCOUNT(3)\tWHEN(4) \r\nbeta \t1d10h5m1s \t33 \t3/1/2014 \t\r\nalpha \t4h35m \t170 \t2013-Feb-03 \t\r\nceta \t33d12h \t9 \t06/Jan/2008 15:04:05 -0700\t\r\n"]
[1.014021, "o", "\r\n% "]
[5.015241, "o", "\u001b[H\u001b[2J\u001b[3J"]
[5.015339, "o", "#\r\n# source tabular data:\r\n"]
[5.015688, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[5.015776, "o", "\r\n#\r\n# orgmode output mode:\r\n\r\n% tablizer -O table.demo1"]
[6.016322, "o", "\r\n"]
[6.01823, "o", "+---------+-------------+----------+----------------------------+\r\n| NAME(1) | DURATION(2) | COUNT(3) | WHEN(4) |\r\n+---------+-------------+----------+----------------------------+\r\n| beta | 1d10h5m1s | 33 | 3/1/2014 |\r\n| alpha | 4h35m | 170 | 2013-Feb-03 |\r\n| ceta | 33d12h | 9 | 06/Jan/2008 15:04:05 -0700 |\r\n+---------+-------------+----------+----------------------------+\r\n"]
[6.018497, "o", "\r\n% "]
[10.020014, "o", "\u001b[H\u001b[2J\u001b[3J"]
[10.020112, "o", "#\r\n# source tabular data:\r\n"]
[10.020573, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[10.020643, "o", "\r\n#\r\n"]
[10.02068, "o", "# markdown output mode:\r\n\r\n% tablizer -M table.demo1"]
[11.021559, "o", "\r\n"]
[11.023551, "o", "| NAME(1) | DURATION(2) | COUNT(3) | WHEN(4) |\r\n|---------|-------------|----------|----------------------------|\r\n| beta | 1d10h5m1s | 33 | 3/1/2014 |\r\n| alpha | 4h35m | 170 | 2013-Feb-03 |\r\n| ceta | 33d12h | 9 | 06/Jan/2008 15:04:05 -0700 |\r\n"]
[11.023838, "o", "\r\n% "]
[15.025244, "o", "\u001b[H\u001b[2J\u001b[3J"]
[15.025345, "o", "#\r\n# source tabular data:\r\n"]
[15.025829, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[15.025915, "o", "\r\n#\r\n# shell output mode:\r\n"]
[15.025931, "o", "\r\n"]
[15.025948, "o", "% tablizer -S table.demo1"]
[16.026714, "o", "\r\n"]
[16.028606, "o", "NAME(1)=\"beta\" DURATION(2)=\"1d10h5m1s\" COUNT(3)=\"33\" WHEN(4)=\"3/1/2014\"\r\nNAME(1)=\"alpha\" DURATION(2)=\"4h35m\" COUNT(3)=\"170\" WHEN(4)=\"2013-Feb-03\"\r\nNAME(1)=\"ceta\" DURATION(2)=\"33d12h\" COUNT(3)=\"9\" WHEN(4)=\"06/Jan/2008 15:04:05 -0700\"\r\n"]
[16.029144, "o", "\r\n% "]
[20.030593, "o", "\u001b[H\u001b[2J\u001b[3J"]
[20.030706, "o", "#\r\n# source tabular data:\r\n"]
[20.03121, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[20.031277, "o", "\r\n#\r\n# extended output mode:\r\n"]
[20.031327, "o", "\r\n% tablizer -X table.demo1"]
[21.032053, "o", "\r\n"]
[21.033787, "o", " NAME(1): beta\r\nDURATION(2): 1d10h5m1s\r\n COUNT(3): 33\r\n WHEN(4): 3/1/2014\r\n\r\n NAME(1): alpha\r\nDURATION(2): 4h35m\r\n COUNT(3): 170\r\n WHEN(4): 2013-Feb-03\r\n\r\n NAME(1): ceta\r\nDURATION(2): 33d12h\r\n COUNT(3): 9\r\n WHEN(4): 06/Jan/2008 15:04:05 -0700\r\n\r\n"]
[21.034132, "o", "\r\n% "]
[25.035531, "o", "\u001b[H\u001b[2J\u001b[3J"]
[25.035585, "o", "#\r\n"]
[25.035681, "o", "# source tabular data:\r\n"]
[25.036179, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[25.036232, "o", "\r\n#\r\n"]
[25.036274, "o", "# yaml output mode:\r\n\r\n% tablizer -Y table.demo1"]
[26.036928, "o", "\r\n"]
[26.038674, "o", "entries:\r\n - count: 33\r\n duration: \"1d10h5m1s\"\r\n name: \"beta\"\r\n when: \"3/1/2014\"\r\n - count: 170\r\n duration: \"4h35m\"\r\n name: \"alpha\"\r\n when: \"2013-Feb-03\"\r\n - count: 9\r\n duration: \"33d12h\"\r\n name: \"ceta\"\r\n when: \"06/Jan/2008 15:04:05 -0700\"\r\n"]
[26.038975, "o", "\r\n% "]
[30.040539, "o", "\u001b[H\u001b[2J\u001b[3J"]
[30.040659, "o", "#\r\n# source tabular data:\r\n"]
[30.041167, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[30.041246, "o", "\r\n#\r\n# CSV output mode:\r\n\r\n% tablizer -C table.demo1"]
[31.042088, "o", "\r\n"]
[31.043721, "o", "NAME,DURATION,COUNT,WHEN\r\nbeta,1d10h5m1s,33,3/1/2014\r\nalpha,4h35m,170,2013-Feb-03\r\nceta,33d12h,9,06/Jan/2008 15:04:05 -0700\r\n"]
[31.043997, "o", "\r\n% "]
[35.045523, "o", "\u001b[H\u001b[2J\u001b[3J"]
[35.04563, "o", "#\r\n# source tabular data:\r\n"]
[35.046209, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[35.046275, "o", "\r\n#\r\n# sort by column 3:\r\n\r\n% tablizer -A -k 3 table.demo1"]
[36.047083, "o", "\r\n"]
[36.048793, "o", "NAME(1)\tDURATION(2)\tCOUNT(3)\tWHEN(4) \r\nalpha \t4h35m \t170 \t2013-Feb-03 \t\r\nbeta \t1d10h5m1s \t33 \t3/1/2014 \t\r\nceta \t33d12h \t9 \t06/Jan/2008 15:04:05 -0700\t\r\n"]
[36.049077, "o", "\r\n% "]
[40.050739, "o", "\u001b[H\u001b[2J\u001b[3J"]
[40.050925, "o", "#\r\n# source tabular data:\r\n"]
[40.051481, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[40.051671, "o", "\r\n#\r\n# sort by column 4 and sort type time:\r\n\r\n% tablizer -A -k 4 -t table.demo1"]
[41.052486, "o", "\r\n"]
[41.05454, "o", "NAME(1)\tDURATION(2)\tCOUNT(3)\tWHEN(4) \r\nceta \t33d12h \t9 \t06/Jan/2008 15:04:05 -0700\t\r\nalpha \t4h35m \t170 \t2013-Feb-03 \t\r\nbeta \t1d10h5m1s \t33 \t3/1/2014 \t\r\n"]
[41.054864, "o", "\r\n% "]
[45.056297, "o", "\u001b[H\u001b[2J\u001b[3J"]
[45.056405, "o", "#\r\n# source tabular data:\r\n"]
[45.056895, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[45.056978, "o", "\r\n#\r\n"]
[45.057023, "o", "# sort by column 2 and sort type duration:\r\n"]
[45.057073, "o", "\r\n% tablizer -A -k 2 -a table.demo1"]
[46.057895, "o", "\r\n"]
[46.059684, "o", "NAME(1)\tDURATION(2)\tCOUNT(3)\tWHEN(4) \r\nalpha \t4h35m \t170 \t2013-Feb-03 \t\r\nbeta \t1d10h5m1s \t33 \t3/1/2014 \t\r\nceta \t33d12h \t9 \t06/Jan/2008 15:04:05 -0700\t\r\n"]
[46.059988, "o", "\r\n% "]
[50.061514, "o", "\u001b[H\u001b[2J\u001b[3J"]
[50.061622, "o", "#\r\n# source tabular data:\r\n"]
[50.062091, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[50.062188, "o", "\r\n#\r\n# only display column 1 and 3:\r\n\r\n% tablizer -A -c 1,3 table.demo1"]
[51.062985, "o", "\r\n"]
[51.066293, "o", "NAME(1)\tCOUNT(3) \r\nbeta \t33 \t\r\nalpha \t170 \t\r\nceta \t9 \t\r\n"]
[51.066843, "o", "\r\n% "]
[55.070781, "o", "\u001b[H\u001b[2J\u001b[3J"]
[55.071327, "o", "#\r\n# source tabular data:\r\n"]
[55.073499, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[55.073822, "o", "\r\n#\r\n# only display columns matching /(RA|AM)/:\r\n"]
[55.074188, "o", "\r\n% tablizer -A -c AM,RA table.demo1"]
[56.07636, "o", "\r\n"]
[56.078603, "o", "NAME(1)\tDURATION(2) \r\nbeta \t1d10h5m1s \t\r\nalpha \t4h35m \t\r\nceta \t33d12h \t\r\n"]
[56.078957, "o", "\r\n% "]
[60.080574, "o", "\u001b[H\u001b[2J\u001b[3J"]
[60.080734, "o", "#\r\n# source tabular data:\r\n"]
[60.081286, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[60.081418, "o", "\r\n#\r\n# only display column 1 and 3 in extended mode:\r\n\r\n% tablizer -X -c 1,3 table.demo1"]
[61.082844, "o", "\r\n"]
[61.089822, "o", " NAME(1): beta\r\nCOUNT(3): 33\r\n\r\n NAME(1): alpha\r\nCOUNT(3): 170\r\n\r\n NAME(1): ceta\r\nCOUNT(3): 9\r\n\r\n"]
[61.090969, "o", "\r\n% "]
[65.096092, "o", "\u001b[H\u001b[2J\u001b[3J"]
[65.096571, "o", "#\r\n# source tabular data:\r\n"]
[65.098736, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[65.099085, "o", "\r\n#\r\n# only show rows matching /20:\r\n"]
[65.099283, "o", "\r\n% tablizer /20 -A table.demo1"]
[66.101537, "o", "\r\n"]
[66.109112, "o", "NAME(1)\tDURATION(2)\tCOUNT(3)\tWHEN(4) \r\nbeta \t1d10h5m1s \t33 \t3/1\u001b[102;30m/20\u001b[0m14 \t\r\nceta \t33d12h \t9 \t06/Jan\u001b[102;30m/20\u001b[0m08 15:04:05 -0700\t\r\n"]
[66.109405, "o", "\r\n% "]
[70.11076, "o", "\u001b[H\u001b[2J\u001b[3J"]
[70.110873, "o", "#\r\n# source tabular data:\r\n"]
[70.111365, "o", "NAME DURATION COUNT WHEN\r\nbeta 1d10h5m1s 33 3/1/2014\r\nalpha 4h35m 170 2013-Feb-03\r\nceta 33d12h 9 06/Jan/2008 15:04:05 -0700\r\n"]
[70.111469, "o", "\r\n#\r\n# only show rows NOT matching /20:\r\n\r\n% tablizer /20 -A -v table.demo1"]
[71.112738, "o", "\r\n"]
[71.120032, "o", "NAME(1)\tDURATION(2)\tCOUNT(3)\tWHEN(4) \r\nalpha \t4h35m \t170 \t2013-Feb-03\t\r\n"]
[71.121127, "o", "\r\n% "]
[75.126199, "o", "\u001b[H\u001b[2J\u001b[3J"]

BIN
demo/tablizer-demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

17
go.mod
View File

@@ -3,19 +3,22 @@ module github.com/tlinden/tablizer
go 1.18 go 1.18
require ( require (
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 github.com/alecthomas/repr v0.1.1
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/gookit/color v1.5.2 github.com/gookit/color v1.5.2
github.com/olekukonko/tablewriter v0.0.5 github.com/olekukonko/tablewriter v0.0.5
github.com/spf13/cobra v1.5.0 github.com/spf13/cobra v1.6.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/mattn/go-runewidth v0.0.10 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/rivo/uniseg v0.1.0 // indirect
// force release. > 0.4. doesnt build everywhere, see:
// https://github.com/TLINDEN/tablizer/actions/runs/3396457307/jobs/5647544615
github.com/rivo/uniseg v0.2.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 // indirect golang.org/x/sys v0.1.0 // indirect
) )

26
go.sum
View File

@@ -1,5 +1,5 @@
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY= github.com/alecthomas/repr v0.1.1 h1:87P60cSmareLAxMc4Hro0r2RBY4ROm0dYwkJNpS4pPs=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/repr v0.1.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= 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/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -8,21 +8,23 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 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/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 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/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -31,13 +33,15 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c= 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=
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -20,7 +20,6 @@ package lib
// contains a whole parsed table // contains a whole parsed table
type Tabdata struct { type Tabdata struct {
maxwidthHeader int // longest header maxwidthHeader int // longest header
maxwidthPerCol []int // max width per column
columns int // count columns int // count
headers []string // [ "ID", "NAME", ...] headers []string // [ "ID", "NAME", ...]
entries [][]string entries [][]string

View File

@@ -155,17 +155,10 @@ func trimRow(row []string) []string {
func colorizeData(c cfg.Config, output string) string { func colorizeData(c cfg.Config, output string) string {
if len(c.Pattern) > 0 && !c.NoColor && color.IsConsole(os.Stdout) { if len(c.Pattern) > 0 && !c.NoColor && color.IsConsole(os.Stdout) {
r := regexp.MustCompile("(" + c.Pattern + ")") r := regexp.MustCompile("(" + c.Pattern + ")")
return r.ReplaceAllString(output, "<bg="+c.MatchBG+";fg="+c.MatchFG+">$1</>") return r.ReplaceAllStringFunc(output, func(in string) string {
return c.ColorStyle.Sprint(in)
})
} else { } else {
return output return output
} }
} }
func isTerminal(f *os.File) bool {
o, _ := f.Stat()
if (o.Mode() & os.ModeCharDevice) == os.ModeCharDevice {
return true
} else {
return false
}
}

View File

@@ -48,11 +48,6 @@ func TestContains(t *testing.T) {
func TestPrepareColumns(t *testing.T) { func TestPrepareColumns(t *testing.T) {
data := Tabdata{ data := Tabdata{
maxwidthHeader: 5, maxwidthHeader: 5,
maxwidthPerCol: []int{
5,
5,
8,
},
columns: 3, columns: 3,
headers: []string{ headers: []string{
"ONE", "TWO", "THREE", "ONE", "TWO", "THREE",

View File

@@ -19,57 +19,62 @@ package lib
import ( import (
"errors" "errors"
"github.com/gookit/color"
"github.com/tlinden/tablizer/cfg" "github.com/tlinden/tablizer/cfg"
"io" "io"
"os" "os"
) )
func ProcessFiles(c cfg.Config, args []string) error { func ProcessFiles(c *cfg.Config, args []string) error {
fds, pattern, err := determineIO(&c, args) fds, pattern, err := determineIO(c, args)
if err != nil { if err != nil {
return err return err
} }
determineColormode(&c) if err := c.PreparePattern(pattern); err != nil {
return err
}
for _, fd := range fds { for _, fd := range fds {
data, err := parseFile(c, fd, pattern) data, err := Parse(*c, fd)
if err != nil { if err != nil {
return err return err
} }
err = PrepareColumns(&c, &data) err = PrepareColumns(c, &data)
if err != nil { if err != nil {
return err return err
} }
printData(os.Stdout, c, &data) printData(os.Stdout, *c, &data)
} }
return nil return nil
} }
// find supported color mode, modifies config based on constants
func determineColormode(c *cfg.Config) {
if !isTerminal(os.Stdout) {
color.Disable()
} else {
level := color.TermColorLevel()
colors := cfg.Colors()
c.MatchFG = colors[level]["fg"]
c.MatchBG = colors[level]["bg"]
}
}
func determineIO(c *cfg.Config, args []string) ([]io.Reader, string, error) { func determineIO(c *cfg.Config, args []string) ([]io.Reader, string, error) {
var pattern string var pattern string
var fds []io.Reader var fds []io.Reader
var havefiles bool var haveio bool
stat, _ := os.Stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
// we're reading from STDIN, which takes precedence over file args
fds = append(fds, os.Stdin)
if len(args) > 0 {
// ignore any args > 1
pattern = args[0]
c.Pattern = args[0] // used for colorization by printData()
}
haveio = true
} else {
if len(args) > 0 { if len(args) > 0 {
// threre were args left, take a look // threre were args left, take a look
if args[0] == "-" {
// in traditional unix programs a dash denotes STDIN (forced)
fds = append(fds, os.Stdin)
haveio = true
} else {
if _, err := os.Stat(args[0]); err != nil { if _, err := os.Stat(args[0]); err != nil {
// first one is not a file, consider it as regexp and // first one is not a file, consider it as regexp and
// shift arg list // shift arg list
@@ -79,8 +84,9 @@ func determineIO(c *cfg.Config, args []string) ([]io.Reader, string, error) {
} }
if len(args) > 0 { if len(args) > 0 {
// only files // consider any other args as files
for _, file := range args { for _, file := range args {
fd, err := os.OpenFile(file, os.O_RDONLY, 0755) fd, err := os.OpenFile(file, os.O_RDONLY, 0755)
if err != nil { if err != nil {
@@ -88,19 +94,16 @@ func determineIO(c *cfg.Config, args []string) ([]io.Reader, string, error) {
} }
fds = append(fds, fd) fds = append(fds, fd)
haveio = true
}
}
} }
havefiles = true
} }
} }
if !havefiles { if !haveio {
stat, _ := os.Stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
fds = append(fds, os.Stdin)
} else {
return nil, "", errors.New("No file specified and nothing to read on stdin!") return nil, "", errors.New("No file specified and nothing to read on stdin!")
} }
}
return fds, pattern, nil return fds, pattern, nil
} }

View File

@@ -19,6 +19,7 @@ package lib
import ( import (
"bufio" "bufio"
"encoding/csv"
"errors" "errors"
"fmt" "fmt"
"github.com/alecthomas/repr" "github.com/alecthomas/repr"
@@ -28,20 +29,84 @@ import (
"strings" "strings"
) )
/*
Parser switch
*/
func Parse(c cfg.Config, input io.Reader) (Tabdata, error) {
if len(c.Separator) == 1 {
return parseCSV(c, input)
}
return parseTabular(c, input)
}
/*
Parse CSV input.
*/
func parseCSV(c cfg.Config, input io.Reader) (Tabdata, error) {
var content io.Reader = input
data := Tabdata{}
if len(c.Pattern) > 0 {
scanner := bufio.NewScanner(input)
lines := []string{}
hadFirst := false
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if hadFirst {
// don't match 1st line, it's the header
if c.PatternR.MatchString(line) == c.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
}
}
lines = append(lines, line)
hadFirst = true
}
content = strings.NewReader(strings.Join(lines, "\n"))
}
csvreader := csv.NewReader(content)
csvreader.Comma = rune(c.Separator[0])
records, err := csvreader.ReadAll()
if err != nil {
return data, errors.Unwrap(fmt.Errorf("Could not parse CSV input: %w", err))
}
if len(records) >= 1 {
data.headers = records[0]
data.columns = len(records)
for _, head := range data.headers {
// register widest header field
headerlen := len(head)
if headerlen > data.maxwidthHeader {
data.maxwidthHeader = headerlen
}
}
if len(records) > 1 {
data.entries = records[1:]
}
}
return data, nil
}
/* /*
Parse tabular input. Parse tabular input.
*/ */
func parseFile(c cfg.Config, input io.Reader, pattern string) (Tabdata, error) { func parseTabular(c cfg.Config, input io.Reader) (Tabdata, error) {
data := Tabdata{} data := Tabdata{}
var scanner *bufio.Scanner var scanner *bufio.Scanner
hadFirst := false hadFirst := false
separate := regexp.MustCompile(c.Separator) separate := regexp.MustCompile(c.Separator)
patternR, err := regexp.Compile(pattern)
if err != nil {
return data, errors.Unwrap(fmt.Errorf("Regexp pattern %s is invalid: %w", pattern, err))
}
scanner = bufio.NewScanner(input) scanner = bufio.NewScanner(input)
@@ -76,8 +141,8 @@ func parseFile(c cfg.Config, input io.Reader, pattern string) (Tabdata, error) {
} }
} else { } else {
// data processing // data processing
if len(pattern) > 0 { if len(c.Pattern) > 0 {
if patternR.MatchString(line) == c.InvertMatch { if c.PatternR.MatchString(line) == c.InvertMatch {
// by default -v is false, so if a line does NOT // by default -v is false, so if a line does NOT
// match the pattern, we will ignore it. However, // match the pattern, we will ignore it. However,
// if the user specified -v, the matching is inverted, // if the user specified -v, the matching is inverted,
@@ -89,16 +154,6 @@ func parseFile(c cfg.Config, input io.Reader, pattern string) (Tabdata, error) {
idx := 0 // we cannot use the header index, because we could exclude columns idx := 0 // we cannot use the header index, because we could exclude columns
values := []string{} values := []string{}
for _, part := range parts { for _, part := range parts {
width := len(strings.TrimSpace(part))
if len(data.maxwidthPerCol)-1 < idx {
data.maxwidthPerCol = append(data.maxwidthPerCol, width)
} else {
if width > data.maxwidthPerCol[idx] {
data.maxwidthPerCol[idx] = width
}
}
// if Debug { // if Debug {
// fmt.Printf("<%s> ", value) // fmt.Printf("<%s> ", value)
// } // }

View File

@@ -25,40 +25,58 @@ import (
"testing" "testing"
) )
var input = []struct {
name string
text string
separator string
}{
{
name: "tabular-data",
separator: cfg.DefaultSeparator,
text: `
ONE TWO THREE
asd igig cxxxncnc
19191 EDD 1 X`,
},
{
name: "csv-data",
separator: ",",
text: `
ONE,TWO,THREE
asd,igig,cxxxncnc
19191,"EDD 1",X`,
},
}
func TestParser(t *testing.T) { func TestParser(t *testing.T) {
data := Tabdata{ data := Tabdata{
maxwidthHeader: 5, maxwidthHeader: 5,
maxwidthPerCol: []int{
5, 5, 8,
},
columns: 3, columns: 3,
headers: []string{ headers: []string{
"ONE", "TWO", "THREE", "ONE", "TWO", "THREE",
}, },
entries: [][]string{ entries: [][]string{
{ {"asd", "igig", "cxxxncnc"},
"asd", "igig", "cxxxncnc", {"19191", "EDD 1", "X"},
},
{
"19191", "EDD 1", "X",
},
}, },
} }
table := `ONE TWO THREE for _, in := range input {
asd igig cxxxncnc testname := fmt.Sprintf("parse-%s", in.name)
19191 EDD 1 X` t.Run(testname, func(t *testing.T) {
readFd := strings.NewReader(strings.TrimSpace(in.text))
readFd := strings.NewReader(table) c := cfg.Config{Separator: in.separator}
c := cfg.Config{Separator: cfg.DefaultSeparator} gotdata, err := Parse(c, readFd)
gotdata, err := parseFile(c, readFd, "")
if err != nil { if err != nil {
t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata) t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata)
} }
if !reflect.DeepEqual(data, gotdata) { if !reflect.DeepEqual(data, gotdata) {
t.Errorf("Parser returned invalid data, Regex: %s\nExp: %+v\nGot: %+v\n", c.Separator, data, gotdata) t.Errorf("Parser returned invalid data\nExp: %+v\nGot: %+v\n",
data, gotdata)
}
})
} }
} }
@@ -71,48 +89,37 @@ func TestParserPatternmatching(t *testing.T) {
}{ }{
{ {
entries: [][]string{ entries: [][]string{
{ {"asd", "igig", "cxxxncnc"},
"asd", "igig", "cxxxncnc",
},
}, },
pattern: "ig", pattern: "ig",
invert: false, invert: false,
}, },
{ {
entries: [][]string{ entries: [][]string{
{ {"19191", "EDD 1", "X"},
"19191", "EDD 1", "X",
},
}, },
pattern: "ig", pattern: "ig",
invert: true, invert: true,
}, },
{
entries: [][]string{
{
"asd", "igig", "cxxxncnc",
},
},
pattern: "[a-z",
want: true,
},
} }
table := `ONE TWO THREE for _, in := range input {
asd igig cxxxncnc
19191 EDD 1 X`
for _, tt := range tests { for _, tt := range tests {
testname := fmt.Sprintf("parse-with-pattern-%s-inverted-%t", tt.pattern, tt.invert) testname := fmt.Sprintf("parse-%s-with-pattern-%s-inverted-%t",
in.name, tt.pattern, tt.invert)
t.Run(testname, func(t *testing.T) { t.Run(testname, func(t *testing.T) {
c := cfg.Config{InvertMatch: tt.invert, Pattern: tt.pattern, Separator: cfg.DefaultSeparator} c := cfg.Config{InvertMatch: tt.invert, Pattern: tt.pattern,
Separator: in.separator}
readFd := strings.NewReader(table) _ = c.PreparePattern(tt.pattern)
gotdata, err := parseFile(c, readFd, tt.pattern)
readFd := strings.NewReader(strings.TrimSpace(in.text))
gotdata, err := Parse(c, readFd)
if err != nil { if err != nil {
if !tt.want { if !tt.want {
t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata) t.Errorf("Parser returned error: %s\nData processed so far: %+v",
err, gotdata)
} }
} else { } else {
if !reflect.DeepEqual(tt.entries, gotdata.entries) { if !reflect.DeepEqual(tt.entries, gotdata.entries) {
@@ -122,35 +129,30 @@ asd igig cxxxncnc
} }
}) })
} }
}
} }
func TestParserIncompleteRows(t *testing.T) { func TestParserIncompleteRows(t *testing.T) {
data := Tabdata{ data := Tabdata{
maxwidthHeader: 5, maxwidthHeader: 5,
maxwidthPerCol: []int{
5, 5, 1,
},
columns: 3, columns: 3,
headers: []string{ headers: []string{
"ONE", "TWO", "THREE", "ONE", "TWO", "THREE",
}, },
entries: [][]string{ entries: [][]string{
{ {"asd", "igig", ""},
"asd", "igig", "", {"19191", "EDD 1", "X"},
},
{
"19191", "EDD 1", "X",
},
}, },
} }
table := `ONE TWO THREE table := `
ONE TWO THREE
asd igig asd igig
19191 EDD 1 X` 19191 EDD 1 X`
readFd := strings.NewReader(table) readFd := strings.NewReader(strings.TrimSpace(table))
c := cfg.Config{Separator: cfg.DefaultSeparator} c := cfg.Config{Separator: cfg.DefaultSeparator}
gotdata, err := parseFile(c, readFd, "") gotdata, err := Parse(c, readFd)
if err != nil { if err != nil {
t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata) t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata)

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package lib package lib
import ( import (
"encoding/csv"
"fmt" "fmt"
"github.com/gookit/color" "github.com/gookit/color"
"github.com/olekukonko/tablewriter" "github.com/olekukonko/tablewriter"
@@ -43,21 +44,24 @@ func printData(w io.Writer, c cfg.Config, data *Tabdata) {
sortTable(c, data) sortTable(c, data)
switch c.OutputMode { switch c.OutputMode {
case "extended": case cfg.Extended:
printExtendedData(w, c, data) printExtendedData(w, c, data)
case "ascii": case cfg.Ascii:
printAsciiData(w, c, data) printAsciiData(w, c, data)
case "orgtbl": case cfg.Orgtbl:
printOrgmodeData(w, c, data) printOrgmodeData(w, c, data)
case "markdown": case cfg.Markdown:
printMarkdownData(w, c, data) printMarkdownData(w, c, data)
case "shell": case cfg.Shell:
printShellData(w, c, data) printShellData(w, c, data)
case "yaml": case cfg.Yaml:
printYamlData(w, c, data) printYamlData(w, c, data)
case cfg.CSV:
printCSVData(w, c, data)
default: default:
printAsciiData(w, c, data) printAsciiData(w, c, data)
} }
} }
func output(w io.Writer, str string) { func output(w io.Writer, str string) {
@@ -185,7 +189,7 @@ func printShellData(w io.Writer, c cfg.Config, data *Tabdata) {
} }
} }
// no colrization here // no colorization here
output(w, out) output(w, out)
} }
@@ -224,3 +228,23 @@ func printYamlData(w io.Writer, c cfg.Config, data *Tabdata) {
output(w, string(yamlstr)) output(w, string(yamlstr))
} }
func printCSVData(w io.Writer, c cfg.Config, data *Tabdata) {
csvout := csv.NewWriter(w)
if err := csvout.Write(data.headers); err != nil {
log.Fatalln("error writing record to csv:", err)
}
for _, entry := range data.entries {
if err := csvout.Write(entry); err != nil {
log.Fatalln("error writing record to csv:", err)
}
}
csvout.Flush()
if err := csvout.Error(); err != nil {
log.Fatal(err)
}
}

View File

@@ -29,12 +29,6 @@ import (
func newData() Tabdata { func newData() Tabdata {
return Tabdata{ return Tabdata{
maxwidthHeader: 8, maxwidthHeader: 8,
maxwidthPerCol: []int{
5,
9,
3,
26,
},
columns: 4, columns: 4,
headers: []string{ headers: []string{
"NAME", "NAME",
@@ -72,24 +66,33 @@ var tests = []struct {
column int // sort by this column, 0 == default first or NO Sort column int // sort by this column, 0 == default first or NO Sort
desc bool // sort in descending order, default == ascending desc bool // sort in descending order, default == ascending
nonum bool // hide numbering nonum bool // hide numbering
mode string // shell, orgtbl, etc. empty == default: ascii mode int // shell, orgtbl, etc. empty == default: ascii
usecol []int // columns to display, empty == display all usecol []int // columns to display, empty == display all
usecolstr string // for testname, must match usecol usecolstr string // for testname, must match usecol
expect string // rendered output we expect expect string // rendered output we expect
}{ }{
// --------------------- Default settings mode tests `` // --------------------- Default settings mode tests ``
{ {
mode: "ascii", mode: cfg.Ascii,
name: "default", name: "default",
expect: ` expect: `
NAME(1) DURATION(2) COUNT(3) WHEN(4) NAME(1) DURATION(2) COUNT(3) WHEN(4)
beta 1d10h5m1s 33 3/1/2014 beta 1d10h5m1s 33 3/1/2014
alpha 4h35m 170 2013-Feb-03 alpha 4h35m 170 2013-Feb-03
ceta 33d12h 9 06/Jan/2008 15:04:05 -0700`, ceta 33d12h 9 06/Jan/2008 15:04:05 -0700`,
},
{
mode: cfg.CSV,
name: "csv",
expect: `
NAME,DURATION,COUNT,WHEN
beta,1d10h5m1s,33,3/1/2014
alpha,4h35m,170,2013-Feb-03
ceta,33d12h,9,06/Jan/2008 15:04:05 -0700`,
}, },
{ {
name: "default", name: "default",
mode: "orgtbl", mode: cfg.Orgtbl,
expect: ` expect: `
+---------+-------------+----------+----------------------------+ +---------+-------------+----------+----------------------------+
| NAME(1) | DURATION(2) | COUNT(3) | WHEN(4) | | NAME(1) | DURATION(2) | COUNT(3) | WHEN(4) |
@@ -101,7 +104,7 @@ ceta 33d12h 9 06/Jan/2008 15:04:05 -0700`,
}, },
{ {
name: "default", name: "default",
mode: "markdown", mode: cfg.Markdown,
expect: ` expect: `
| NAME(1) | DURATION(2) | COUNT(3) | WHEN(4) | | NAME(1) | DURATION(2) | COUNT(3) | WHEN(4) |
|---------|-------------|----------|----------------------------| |---------|-------------|----------|----------------------------|
@@ -111,7 +114,7 @@ ceta 33d12h 9 06/Jan/2008 15:04:05 -0700`,
}, },
{ {
name: "default", name: "default",
mode: "shell", mode: cfg.Shell,
nonum: true, nonum: true,
expect: ` expect: `
NAME="beta" DURATION="1d10h5m1s" COUNT="33" WHEN="3/1/2014" NAME="beta" DURATION="1d10h5m1s" COUNT="33" WHEN="3/1/2014"
@@ -120,7 +123,7 @@ NAME="ceta" DURATION="33d12h" COUNT="9" WHEN="06/Jan/2008 15:04:05 -0700"`,
}, },
{ {
name: "default", name: "default",
mode: "yaml", mode: cfg.Yaml,
nonum: true, nonum: true,
expect: ` expect: `
entries: entries:
@@ -139,7 +142,7 @@ entries:
}, },
{ {
name: "default", name: "default",
mode: "extended", mode: cfg.Extended,
expect: ` expect: `
NAME(1): beta NAME(1): beta
DURATION(2): 1d10h5m1s DURATION(2): 1d10h5m1s
@@ -248,7 +251,7 @@ DURATION(2) WHEN(4)
func TestPrinter(t *testing.T) { func TestPrinter(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
testname := fmt.Sprintf("print-sortcol-%d-desc-%t-sortby-%s-mode-%s-usecolumns-%s", testname := fmt.Sprintf("print-sortcol-%d-desc-%t-sortby-%s-mode-%d-usecolumns-%s",
tt.column, tt.desc, tt.sortby, tt.mode, tt.usecolstr) tt.column, tt.desc, tt.sortby, tt.mode, tt.usecolstr)
t.Run(testname, func(t *testing.T) { t.Run(testname, func(t *testing.T) {
// replaces os.Stdout, but we ignore it // replaces os.Stdout, but we ignore it
@@ -265,6 +268,8 @@ func TestPrinter(t *testing.T) {
NoColor: true, NoColor: true,
} }
c.ApplyDefaults()
// the test checks the len! // the test checks the len!
if len(tt.usecol) > 0 { if len(tt.usecol) > 0 {
c.Columns = "yes" c.Columns = "yes"

45
t/test.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/sh
# simple commandline unit test script
t="../tablizer"
fail=0
ex() {
# execute a test, report+exit on error, stay silent otherwise
log="/tmp/test-tablizer.$$.log"
name=$1
shift
echo -n "TEST $name "
$* > $log 2>&1
if test $? -ne 0; then
echo "failed, see $log"
fail=1
else
echo "ok"
rm -f $log
fi
}
# only use files in test dir
cd $(dirname $0)
echo "Executing commandline tests ..."
# io pattern tests
ex io-pattern-and-file $t bk7 testtable.kube
cat testtable.kube | ex io-pattern-and-stdin $t bk7
cat testtable.kube | ex io-pattern-and-stdin-dash $t bk7 -
# same w/o pattern
ex io-just-file $t testtable.kube
cat testtable.kube | ex io-just-stdin $t
cat testtable.kube | ex io-just-stdin-dash $t -
if test $fail -ne 0; then
echo "!!! Some tests failed !!!"
exit 1
fi

6
t/testtable Normal file
View File

@@ -0,0 +1,6 @@
NAME READY STATUS RESTARTS AGE
alertmanager-kube-prometheus-alertmanager-0 2/2 Running 35 (45m ago) 11d
grafana-fcc54cbc9-bk7s8 1/1 Running 17 (45m ago) 1d
kube-prometheus-blackbox-exporter-5d85b5d8f4-tskh7 1/1 Running 17 (45m ago) 1h44m
kube-prometheus-kube-state-metrics-b4cd9487-75p7f 1/1 Running 20 (45m ago) 45m
kube-prometheus-node-exporter-bfzpl 1/1 Running 17 (45m ago) 54s

View File

@@ -133,7 +133,7 @@
.\" ======================================================================== .\" ========================================================================
.\" .\"
.IX Title "TABLIZER 1" .IX Title "TABLIZER 1"
.TH TABLIZER 1 "2022-10-16" "1" "User Commands" .TH TABLIZER 1 "2023-01-23" "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
@@ -146,24 +146,34 @@ tablizer \- Manipulate tabular output of other programs
\& Usage: \& Usage:
\& tablizer [regex] [file, ...] [flags] \& tablizer [regex] [file, ...] [flags]
\& \&
\& Flags: \& Operational Flags:
\& \-c, \-\-columns string Only show the speficied columns (separated by ,) \& \-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 \& \-v, \-\-invert\-match select non\-matching rows
\& \-m, \-\-man Display manual page
\& \-n, \-\-no\-numbering Disable header numbering \& \-n, \-\-no\-numbering Disable header numbering
\& \-N, \-\-no\-color Disable pattern highlighting \& \-N, \-\-no\-color Disable pattern highlighting
\& \-o, \-\-output string Output mode \- one of: orgtbl, markdown, extended, yaml, ascii(default) \& \-s, \-\-separator string Custom field separator
\& \-k, \-\-sort\-by int Sort by column (default: 1)
\&
\& Output Flags (mutually exclusive):
\& \-X, \-\-extended Enable extended output \& \-X, \-\-extended Enable extended output
\& \-M, \-\-markdown Enable markdown table output \& \-M, \-\-markdown Enable markdown table output
\& \-O, \-\-orgtbl Enable org\-mode table output \& \-O, \-\-orgtbl Enable org\-mode table output
\& \-s, \-\-separator string Custom field separator \& \-S, \-\-shell Enable shell evaluable ouput
\& \-Y, \-\-yaml Enable yaml output
\& \-C, \-\-csv Enable CSV output
\& \-A, \-\-ascii Default output mode, ascii tabular
\&
\& Sort Mode Flags (mutually exclusive):
\& \-a, \-\-sort\-age sort according to age (duration) string \& \-a, \-\-sort\-age sort according to age (duration) string
\& \-k, \-\-sort\-by int Sort by column (default: 1)
\& \-D, \-\-sort\-desc Sort in descending order (default: ascending) \& \-D, \-\-sort\-desc Sort in descending order (default: ascending)
\& \-i, \-\-sort\-numeric sort according to string numerical value \& \-i, \-\-sort\-numeric sort according to string numerical value
\& \-t, \-\-sort\-time sort according to time string \& \-t, \-\-sort\-time sort according to time string
\&
\& Other Flags:
\& \-\-completion <shell> Generate the autocompletion script for <shell>
\& \-d, \-\-debug Enable debugging
\& \-h, \-\-help help for tablizer
\& \-m, \-\-man Display manual page
\& \-v, \-\-version Print program version \& \-v, \-\-version Print program version
.Ve .Ve
.SH "DESCRIPTION" .SH "DESCRIPTION"
@@ -345,8 +355,84 @@ You can use this in an eval loop.
.PP .PP
Beside normal ascii mode (the default) and extended mode there are Beside normal ascii mode (the default) and extended mode there are
more output modes available: \fBorgtbl\fR which prints an Emacs org-mode more output modes available: \fBorgtbl\fR which prints an Emacs org-mode
table and \fBmarkdown\fR which prints a Markdown table and \fByaml\fR, which table and \fBmarkdown\fR which prints a Markdown table, \fByaml\fR, which
prints yaml encoding. prints yaml encoding and \s-1CSV\s0 mode, which prints a comma separated
value file.
.SS "\s-1ENVIRONMENT VARIABLES\s0"
.IX Subsection "ENVIRONMENT VARIABLES"
\&\fBtablizer\fR supports certain environment variables which use can use
to influence program behavior. Commandline flags have always
precedence over environment variables.
.IP "<T_NO_HEADER_NUMBERING> \- disable numbering of header fields, like \fB\-n\fR." 4
.IX Item "<T_NO_HEADER_NUMBERING> - disable numbering of header fields, like -n."
.PD 0
.IP "<T_COLUMNS> \- comma separated list of columns to output, like \fB\-c\fR" 4
.IX Item "<T_COLUMNS> - comma separated list of columns to output, like -c"
.IP "<\s-1NO_COLORS\s0> \- disable colorization of matches, like \fB\-N\fR" 4
.IX Item "<NO_COLORS> - disable colorization of matches, like -N"
.PD
.SS "\s-1COMPLETION\s0"
.IX Subsection "COMPLETION"
Shell completion for command line options can be enabled by using the
\&\fB\-\-completion\fR flag. The required parameter is the name of your
shell. Currently supported are: bash, zsh, fish and powershell.
.PP
Detailed instructions:
.IP "Bash:" 4
.IX Item "Bash:"
.Vb 1
\& source <(tablizer \-\-completion bash)
.Ve
.Sp
To load completions for each session, execute once:
.Sp
.Vb 2
\& # Linux:
\& $ tablizer \-\-completion bash > /etc/bash_completion.d/tablizer
\&
\& # macOS:
\& $ tablizer \-\-completion bash > $(brew \-\-prefix)/etc/bash_completion.d/tablizer
.Ve
.IP "Zsh:" 4
.IX Item "Zsh:"
If shell completion is not already enabled in your environment,
you will need to enable it. You can execute the following once:
.Sp
.Vb 1
\& echo "autoload \-U compinit; compinit" >> ~/.zshrc
.Ve
.Sp
To load completions for each session, execute once:
.Sp
.Vb 1
\& $ tablizer \-\-completion zsh > "${fpath[1]}/_tablizer"
.Ve
.Sp
You will need to start a new shell for this setup to take effect.
.IP "fish:" 4
.IX Item "fish:"
.Vb 1
\& tablizer \-\-completion fish | source
.Ve
.Sp
To load completions for each session, execute once:
.Sp
.Vb 1
\& tablizer \-\-completion fish > ~/.config/fish/completions/tablizer.fish
.Ve
.IP "PowerShell:" 4
.IX Item "PowerShell:"
.Vb 1
\& tablizer \-\-completion powershell | Out\-String | Invoke\-Expression
.Ve
.Sp
To load completions for every new session, run:
.Sp
.Vb 1
\& tablizer \-\-completion powershell > tablizer.ps1
.Ve
.Sp
and source this file from your PowerShell profile.
.SH "BUGS" .SH "BUGS"
.IX Header "BUGS" .IX Header "BUGS"
In order to report a bug, unexpected behavior, feature requests In order to report a bug, unexpected behavior, feature requests
@@ -356,15 +442,27 @@ or to submit a patch, please open an issue on github:
.IX Header "LICENSE" .IX Header "LICENSE"
This software is licensed under the \s-1GNU GENERAL PUBLIC LICENSE\s0 version 3. This software is licensed under the \s-1GNU GENERAL PUBLIC LICENSE\s0 version 3.
.PP .PP
Copyright (c) 2022 by Thomas von Dein Copyright (c) 2023 by Thomas von Dein
.PP .PP
This software uses the following \s-1GO\s0 libraries: This software uses the following \s-1GO\s0 modules:
.IP "repr (https://github.com/alecthomas/repr)" 4 .IP "repr (https://github.com/alecthomas/repr)" 4
.IX Item "repr (https://github.com/alecthomas/repr)" .IX Item "repr (https://github.com/alecthomas/repr)"
Released under the \s-1MIT\s0 License, Copyright (c) 2016 Alec Thomas Released under the \s-1MIT\s0 License, Copyright (c) 2016 Alec Thomas
.IP "cobra (https://github.com/spf13/cobra)" 4 .IP "cobra (https://github.com/spf13/cobra)" 4
.IX Item "cobra (https://github.com/spf13/cobra)" .IX Item "cobra (https://github.com/spf13/cobra)"
Released under the Apache 2.0 license, Copyright 2013\-2022 The Cobra Authors Released under the Apache 2.0 license, Copyright 2013\-2022 The Cobra Authors
.IP "dateparse (github.com/araddon/dateparse)" 4
.IX Item "dateparse (github.com/araddon/dateparse)"
Released under the \s-1MIT\s0 License, Copyright (c) 2015\-2017 Aaron Raddon
.IP "color (github.com/gookit/color)" 4
.IX Item "color (github.com/gookit/color)"
Released under the \s-1MIT\s0 License, Copyright (c) 2016 inhere
.IP "tablewriter (github.com/olekukonko/tablewriter)" 4
.IX Item "tablewriter (github.com/olekukonko/tablewriter)"
Released under the \s-1MIT\s0 License, Copyright (c) 201 by Oleku Konko
.IP "yaml (gopkg.in/yaml.v3)" 4
.IX Item "yaml (gopkg.in/yaml.v3)"
Released under the \s-1MIT\s0 License, Copyright (c) 2006\-2011 Kirill Simonov
.SH "AUTHORS" .SH "AUTHORS"
.IX Header "AUTHORS" .IX Header "AUTHORS"
Thomas von Dein \fBtom \s-1AT\s0 vondein \s-1DOT\s0 org\fR Thomas von Dein \fBtom \s-1AT\s0 vondein \s-1DOT\s0 org\fR

View File

@@ -7,24 +7,34 @@ tablizer - Manipulate tabular output of other programs
Usage: Usage:
tablizer [regex] [file, ...] [flags] tablizer [regex] [file, ...] [flags]
Flags: Operational Flags:
-c, --columns string Only show the speficied columns (separated by ,) -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 -v, --invert-match select non-matching rows
-m, --man Display manual page
-n, --no-numbering Disable header numbering -n, --no-numbering Disable header numbering
-N, --no-color Disable pattern highlighting -N, --no-color Disable pattern highlighting
-o, --output string Output mode - one of: orgtbl, markdown, extended, yaml, ascii(default) -s, --separator string Custom field separator
-k, --sort-by int Sort by column (default: 1)
Output Flags (mutually exclusive):
-X, --extended Enable extended output -X, --extended Enable extended output
-M, --markdown Enable markdown table output -M, --markdown Enable markdown table output
-O, --orgtbl Enable org-mode table output -O, --orgtbl Enable org-mode table output
-s, --separator string Custom field separator -S, --shell Enable shell evaluable ouput
-Y, --yaml Enable yaml output
-C, --csv Enable CSV output
-A, --ascii Default output mode, ascii tabular
Sort Mode Flags (mutually exclusive):
-a, --sort-age sort according to age (duration) string -a, --sort-age sort according to age (duration) string
-k, --sort-by int Sort by column (default: 1)
-D, --sort-desc Sort in descending order (default: ascending) -D, --sort-desc Sort in descending order (default: ascending)
-i, --sort-numeric sort according to string numerical value -i, --sort-numeric sort according to string numerical value
-t, --sort-time sort according to time string -t, --sort-time sort according to time string
Other Flags:
--completion <shell> Generate the autocompletion script for <shell>
-d, --debug Enable debugging
-h, --help help for tablizer
-m, --man Display manual page
-v, --version Print program version -v, --version Print program version
@@ -198,8 +208,80 @@ You can use this in an eval loop.
Beside normal ascii mode (the default) and extended mode there are Beside normal ascii mode (the default) and extended mode there are
more output modes available: B<orgtbl> which prints an Emacs org-mode more output modes available: B<orgtbl> which prints an Emacs org-mode
table and B<markdown> which prints a Markdown table and B<yaml>, which table and B<markdown> which prints a Markdown table, B<yaml>, which
prints yaml encoding. prints yaml encoding and CSV mode, which prints a comma separated
value file.
=head2 ENVIRONMENT VARIABLES
B<tablizer> supports certain environment variables which use can use
to influence program behavior. Commandline flags have always
precedence over environment variables.
=over
=item <T_NO_HEADER_NUMBERING> - disable numbering of header fields, like B<-n>.
=item <T_COLUMNS> - comma separated list of columns to output, like B<-c>
=item <NO_COLORS> - disable colorization of matches, like B<-N>
=back
=head2 COMPLETION
Shell completion for command line options can be enabled by using the
B<--completion> flag. The required parameter is the name of your
shell. Currently supported are: bash, zsh, fish and powershell.
Detailed instructions:
=over
=item Bash:
source <(tablizer --completion bash)
To load completions for each session, execute once:
# Linux:
$ tablizer --completion bash > /etc/bash_completion.d/tablizer
# macOS:
$ tablizer --completion bash > $(brew --prefix)/etc/bash_completion.d/tablizer
=item Zsh:
If shell completion is not already enabled in your environment,
you will need to enable it. You can execute the following once:
echo "autoload -U compinit; compinit" >> ~/.zshrc
To load completions for each session, execute once:
$ tablizer --completion zsh > "${fpath[1]}/_tablizer"
You will need to start a new shell for this setup to take effect.
=item fish:
tablizer --completion fish | source
To load completions for each session, execute once:
tablizer --completion fish > ~/.config/fish/completions/tablizer.fish
=item PowerShell:
tablizer --completion powershell | Out-String | Invoke-Expression
To load completions for every new session, run:
tablizer --completion powershell > tablizer.ps1
and source this file from your PowerShell profile.
=back
=head1 BUGS =head1 BUGS
@@ -211,9 +293,9 @@ L<https://github.com/TLINDEN/tablizer/issues>.
This software is licensed under the GNU GENERAL PUBLIC LICENSE version 3. This software is licensed under the GNU GENERAL PUBLIC LICENSE version 3.
Copyright (c) 2022 by Thomas von Dein Copyright (c) 2023 by Thomas von Dein
This software uses the following GO libraries: This software uses the following GO modules:
=over 4 =over 4
@@ -225,6 +307,22 @@ Released under the MIT License, Copyright (c) 2016 Alec Thomas
Released under the Apache 2.0 license, Copyright 2013-2022 The Cobra Authors Released under the Apache 2.0 license, Copyright 2013-2022 The Cobra Authors
=item dateparse (github.com/araddon/dateparse)
Released under the MIT License, Copyright (c) 2015-2017 Aaron Raddon
=item color (github.com/gookit/color)
Released under the MIT License, Copyright (c) 2016 inhere
=item tablewriter (github.com/olekukonko/tablewriter)
Released under the MIT License, Copyright (c) 201 by Oleku Konko
=item yaml (gopkg.in/yaml.v3)
Released under the MIT License, Copyright (c) 2006-2011 Kirill Simonov
=back =back
=head1 AUTHORS =head1 AUTHORS