mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-18 13:01:11 +01:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ce6c30f54 | |||
|
|
ec0b210167 | ||
| 253ef8262e | |||
| da48994744 | |||
| 39f06fddc8 | |||
|
|
50a9378d92 | ||
|
|
35b726fee4 | ||
|
|
8c87da34f2 | ||
|
|
6f0f5afb27 | ||
|
|
62b606e7da | ||
|
|
567d23b175 | ||
|
|
14f24533f0 | ||
|
|
4e413c02b5 | ||
|
|
6d8c0c0936 | ||
|
|
21b607af7c | ||
|
|
06a5d74fb6 | ||
|
|
5f3f7c417c | ||
|
|
687f4b7bb2 | ||
| 24b66b8a6b | |||
| d87c6878a4 | |||
| 4cdc4c8e18 | |||
| 9cb9a66332 | |||
| 24277cd716 | |||
| e51b141032 | |||
| 7af7304529 | |||
| b4c833a0ba | |||
| 1c36d93d65 |
96
.gh-dash.yml
Normal file
96
.gh-dash.yml
Normal file
@@ -0,0 +1,96 @@
|
||||
prSections:
|
||||
- title: Responsible PRs
|
||||
filters: repo:tlinden/tablizer is:open NOT dependabot
|
||||
layout:
|
||||
repoName:
|
||||
hidden: true
|
||||
|
||||
- title: Responsible Dependabot PRs
|
||||
filters: repo:tlinden/tablizer is:open dependabot
|
||||
layout:
|
||||
repoName:
|
||||
hidden: true
|
||||
|
||||
issuesSections:
|
||||
- title: Responsible Issues
|
||||
filters: is:open repo:tlinden/tablizer -author:@me
|
||||
layout:
|
||||
repoName:
|
||||
hidden: true
|
||||
|
||||
- title: Note-to-Self Issues
|
||||
filters: is:open repo:tlinden/tablizer author:@me
|
||||
layout:
|
||||
creator:
|
||||
hidden: true
|
||||
repoName:
|
||||
hidden: true
|
||||
|
||||
defaults:
|
||||
preview:
|
||||
open: false
|
||||
width: 100
|
||||
|
||||
keybindings:
|
||||
universal:
|
||||
- key: "shift+down"
|
||||
builtin: pageDown
|
||||
- key: "shift+up"
|
||||
builtin: pageUp
|
||||
prs:
|
||||
- key: g
|
||||
name: gitu
|
||||
command: >
|
||||
cd {{.RepoPath}} && /home/scip/bin/gitu
|
||||
- key: M
|
||||
name: squash-merge
|
||||
command: gh pr merge --rebase --squash --admin --repo {{.RepoName}} {{.PrNumber}}
|
||||
- key: i
|
||||
name: show ci checks
|
||||
command: gh pr checks --repo {{.RepoName}} {{.PrNumber}} | glow -p
|
||||
- key: e
|
||||
name: edit pr
|
||||
command: ~/.config/gh-dash/edit-gh-pr {{.RepoName}} {{.PrNumber}}
|
||||
- key: E
|
||||
name: open repo in emacs
|
||||
command: emacsclient {{.RepoPath}} &
|
||||
issues:
|
||||
- key: v
|
||||
name: view
|
||||
command: gh issue view --repo {{.RepoName}} {{.IssueNumber}} | glow -p
|
||||
- key: l
|
||||
name: add label
|
||||
command: gh issue --repo {{.RepoName}} edit {{.IssueNumber}} --add-label $(gum choose bug enhancement question dependencies wontfix)
|
||||
- key: L
|
||||
name: remove label
|
||||
command: gh issue --repo {{.RepoName}} edit {{.IssueNumber}} --remove-label $(gum choose bug enhancement question dependencies wontfix)
|
||||
- key: E
|
||||
name: open repo in emacs
|
||||
command: emacsclient {{.RepoPath}} &
|
||||
|
||||
theme:
|
||||
ui:
|
||||
sectionsShowCount: true
|
||||
table:
|
||||
compact: false
|
||||
showSeparator: true
|
||||
colors:
|
||||
text:
|
||||
primary: "#E2E1ED"
|
||||
secondary: "#6770cb"
|
||||
inverted: "#242347"
|
||||
faint: "#b0793b"
|
||||
warning: "#E0AF68"
|
||||
success: "#3DF294"
|
||||
background:
|
||||
selected: "#1B1B33"
|
||||
border:
|
||||
primary: "#383B5B"
|
||||
secondary: "#39386B"
|
||||
faint: "#8d3e0b"
|
||||
|
||||
repoPaths:
|
||||
:owner/:repo: ~/dev/:repo
|
||||
|
||||
pager:
|
||||
diff: delta
|
||||
10
.github/workflows/ci.yaml
vendored
10
.github/workflows/ci.yaml
vendored
@@ -1,16 +1,16 @@
|
||||
name: build-and-test-tablizer
|
||||
on: [push, pull_request]
|
||||
on: [push]
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
version: ['1.23']
|
||||
version: ['1.24']
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
name: Build
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.version }}
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '${{ matrix.version }}'
|
||||
id: go
|
||||
@@ -28,9 +28,9 @@ jobs:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.23
|
||||
go-version: 1.24
|
||||
- uses: actions/checkout@v5
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
|
||||
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
@@ -13,9 +13,9 @@ jobs:
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.22.11
|
||||
go-version: 1.24.0
|
||||
|
||||
- name: Build the executables
|
||||
run: ./mkrel.sh tablizer ${{ github.ref_name}}
|
||||
|
||||
2
Makefile
2
Makefile
@@ -65,7 +65,7 @@ clean:
|
||||
rm -rf $(tool) releases coverage.out
|
||||
|
||||
test: clean
|
||||
go test ./... $(OPTS)
|
||||
go test -count=1 -cover ./... $(OPTS)
|
||||
|
||||
singletest:
|
||||
@echo "Call like this: 'make singletest TEST=TestPrepareColumns MOD=lib'"
|
||||
|
||||
97
README.md
97
README.md
@@ -11,50 +11,59 @@ ignore certain column[s] by regex, name or number. It can output the
|
||||
tabular data in a range of formats (see below). There's even an
|
||||
interactive filter/selection tool available.
|
||||
|
||||
Usage:
|
||||
## Demo
|
||||
|
||||

|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
```default
|
||||
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, --numbering Enable header numbering
|
||||
-N, --no-color Disable pattern highlighting
|
||||
-H, --no-headers Disable headers display
|
||||
-s, --separator string Custom field separator
|
||||
-k, --sort-by int|name Sort by column (default: 1)
|
||||
-z, --fuzzy Use fuzzy search [experimental]
|
||||
-F, --filter field[!]=reg Filter given field with regex, can be used multiple times
|
||||
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
|
||||
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T
|
||||
-I, --interactive Interactively filter and select rows
|
||||
-c, --columns string Only show the speficied columns (separated by ,)
|
||||
-v, --invert-match select non-matching rows
|
||||
-n, --numbering Enable header numbering
|
||||
-N, --no-color Disable pattern highlighting
|
||||
-H, --no-headers Disable headers display
|
||||
-s, --separator <string> Custom field separator
|
||||
-k, --sort-by <int|name> Sort by column (default: 1)
|
||||
-z, --fuzzy Use fuzzy search [experimental]
|
||||
-F, --filter <field[!]=reg> Filter given field with regex, can be used multiple times
|
||||
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
|
||||
-R, --regex-transposer </from/to/> Apply /search/replace/ regexp to fields given in -T
|
||||
-j, --json Read JSON input (must be array of hashes)
|
||||
-I, --interactive Interactively filter and select rows
|
||||
|
||||
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 output
|
||||
-Y, --yaml Enable yaml output
|
||||
-C, --csv Enable CSV output
|
||||
-A, --ascii Default output mode, ascii tabular
|
||||
-L, --hightlight-lines Use alternating background colors for tables
|
||||
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
|
||||
space separated
|
||||
-X, --extended Enable extended output
|
||||
-M, --markdown Enable markdown table output
|
||||
-O, --orgtbl Enable org-mode table output
|
||||
-S, --shell Enable shell evaluable output
|
||||
-Y, --yaml Enable yaml output
|
||||
-C, --csv Enable CSV output
|
||||
-A, --ascii Default output mode, ascii tabular
|
||||
-L, --hightlight-lines Use alternating background colors for tables
|
||||
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
|
||||
space separated
|
||||
--ofs <char> Output field separator, used by -A and -C.
|
||||
|
||||
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
|
||||
-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>
|
||||
-f, --config <file> Configuration file (default: ~/.config/tablizer/config)
|
||||
-d, --debug Enable debugging
|
||||
-h, --help help for tablizer
|
||||
-m, --man Display manual page
|
||||
-V, --version Print program version
|
||||
-r --read-file <file> Use <file> as input instead of STDIN
|
||||
--completion <shell> Generate the autocompletion script for <shell>
|
||||
-f, --config <file> Configuration file (default: ~/.config/tablizer/config)
|
||||
-d, --debug Enable debugging
|
||||
-h, --help help for tablizer
|
||||
-m, --man Display manual page
|
||||
-V, --version Print program version
|
||||
```
|
||||
|
||||
Let's take this output:
|
||||
@@ -71,13 +80,13 @@ to do this with tablizer:
|
||||
|
||||
```
|
||||
% kubectl get pods | tablizer
|
||||
NAME(1) READY(2) STATUS(3) RESTARTS(4) AGE(5)
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
repldepl-7bcd8d5b64-7zq4l 1/1 Running 1 (69m ago) 5h26m
|
||||
repldepl-7bcd8d5b64-m48n8 1/1 Running 1 (69m ago) 5h26m
|
||||
repldepl-7bcd8d5b64-q2bf4 1/1 Running 1 (69m ago) 5h26m
|
||||
|
||||
% kubectl get pods | tablizer -c 1,3
|
||||
NAME(1) STATUS(3)
|
||||
NAME STATUS
|
||||
repldepl-7bcd8d5b64-7zq4l Running
|
||||
repldepl-7bcd8d5b64-m48n8 Running
|
||||
repldepl-7bcd8d5b64-q2bf4 Running
|
||||
@@ -115,14 +124,14 @@ You can also specify a regex pattern to reduce the output:
|
||||
|
||||
```
|
||||
% kubectl get pods | tablizer q2bf4
|
||||
NAME(1) READY(2) STATUS(3) RESTARTS(4) AGE(5)
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
repldepl-7bcd8d5b64-q2bf4 1/1 Running 1 (69m ago) 5h26m
|
||||
```
|
||||
|
||||
Sometimes a filter regex is to broad and you wish to filter only on a
|
||||
particular column. This is possible using `-F`:
|
||||
```
|
||||
% kubectl get pods | tablizer -n -Fname=2
|
||||
% kubectl get pods | tablizer -Fname=2
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
repldepl-7bcd8d5b64-q2bf4 1/1 Running 1 (69m ago) 5h26m
|
||||
```
|
||||
@@ -136,7 +145,7 @@ You can also use it to modify certain cells using regular expression
|
||||
matching. For example:
|
||||
|
||||
```shell
|
||||
kubectl get pods | tablizer -n -T4 -R '/ /-/'
|
||||
kubectl get pods | tablizer -T4 -R '/ /-/'
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
repldepl-7bcd8d5b64-7zq4l 1/1 Running 1-(69m-ago) 5h26m
|
||||
repldepl-7bcd8d5b64-m48n8 1/1 Running 1-(69m-ago) 5h26m
|
||||
@@ -147,11 +156,12 @@ Here, we modified the 4th column (`-T4`) by replacing every space with
|
||||
a dash. If you need to work with `/` characters, you can also use any
|
||||
other separator, for instance: `-R '| |-|'`.
|
||||
|
||||
There's also an interactive mode, invoked with the option B<-I>, where
|
||||
you can interactively filter and select rows:
|
||||
|
||||
<img width="937" height="293" alt="interactive" src="https://github.com/user-attachments/assets/0d4d65e2-d156-43ed-8021-39047c7939ed" />
|
||||
|
||||
|
||||
## Demo
|
||||
|
||||
[](https://asciinema.org/a/9FKc3HPnlg8D2X8otheleEa9t)
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -182,10 +192,9 @@ hesitate to ask me about it, I'll add it.
|
||||
## Documentation
|
||||
|
||||
The documentation is provided as a unix man-page. It will be
|
||||
automatically installed if you install from source. However, you can
|
||||
read the man-page online:
|
||||
automatically installed if you install from source.
|
||||
|
||||
https://github.com/TLINDEN/tablizer/blob/main/tablizer.pod
|
||||
[However, you can read the man-page online](https://github.com/TLINDEN/tablizer/blob/main/tablizer.pod).
|
||||
|
||||
Or if you cloned the repository you can read it this way (perl needs
|
||||
to be installed though): `perldoc tablizer.pod`.
|
||||
|
||||
@@ -27,13 +27,26 @@ import (
|
||||
"github.com/hashicorp/hcl/v2/hclsimple"
|
||||
)
|
||||
|
||||
const DefaultSeparator string = `(\s\s+|\t)`
|
||||
const Version string = "v1.5.1"
|
||||
const MAXPARTS = 2
|
||||
const (
|
||||
Version = "v1.5.9"
|
||||
MAXPARTS = 2
|
||||
)
|
||||
|
||||
var DefaultConfigfile = os.Getenv("HOME") + "/.config/tablizer/config"
|
||||
var (
|
||||
DefaultConfigfile = os.Getenv("HOME") + "/.config/tablizer/config"
|
||||
VERSION string // maintained by -x
|
||||
|
||||
var VERSION string // maintained by -x
|
||||
SeparatorTemplates = map[string]string{
|
||||
":tab:": `\s*\t\s*`, // tab but eats spaces around
|
||||
":spaces:": `\s{2,}`, // 2 or more spaces
|
||||
":pipe:": `\s*\|\s*`, // one pipe eating spaces around
|
||||
":default:": `(\s\s+|\t)`, // 2 or more spaces or tab
|
||||
":nonword:": `\W`, // word boundary
|
||||
":nondigit:": `\D`, // same for numbers
|
||||
":special:": `[\*\+\-_\(\)\[\]\{\}?\\/<>=&$§"':,\^]+`, // match any special char
|
||||
":nonprint:": `[[:^print:]]+`, // non printables
|
||||
}
|
||||
)
|
||||
|
||||
// public config, set via config file or using defaults
|
||||
type Settings struct {
|
||||
@@ -79,6 +92,7 @@ type Config struct {
|
||||
UseFuzzySearch bool
|
||||
UseHighlight bool
|
||||
Interactive bool
|
||||
InputJSON bool
|
||||
|
||||
SortMode string
|
||||
SortDescending bool
|
||||
@@ -112,6 +126,8 @@ type Config struct {
|
||||
|
||||
// -r <file>
|
||||
InputFile string
|
||||
|
||||
OFS string
|
||||
}
|
||||
|
||||
// maps outputmode short flags to output mode, ie. -O => -o orgtbl
|
||||
@@ -353,6 +369,13 @@ func (conf *Config) ApplyDefaults() {
|
||||
if conf.OutputMode == Yaml || conf.OutputMode == CSV {
|
||||
conf.Numbering = false
|
||||
}
|
||||
|
||||
if conf.Separator[0] == ':' && conf.Separator[len(conf.Separator)-1] == ':' {
|
||||
separator, ok := SeparatorTemplates[conf.Separator]
|
||||
if ok {
|
||||
conf.Separator = separator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (conf *Config) PreparePattern(patterns []*Pattern) error {
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"fmt"
|
||||
// "reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPrepareModeFlags(t *testing.T) {
|
||||
@@ -44,9 +46,8 @@ func TestPrepareModeFlags(t *testing.T) {
|
||||
conf := Config{}
|
||||
|
||||
conf.PrepareModeFlags(testdata.flag)
|
||||
if conf.OutputMode != testdata.expect {
|
||||
t.Errorf("got: %d, expect: %d", conf.OutputMode, testdata.expect)
|
||||
}
|
||||
|
||||
assert.EqualValues(t, testdata.expect, conf.OutputMode)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -70,9 +71,7 @@ func TestPrepareSortFlags(t *testing.T) {
|
||||
|
||||
conf.PrepareSortFlags(testdata.flag)
|
||||
|
||||
if conf.SortMode != testdata.expect {
|
||||
t.Errorf("got: %s, expect: %s", conf.SortMode, testdata.expect)
|
||||
}
|
||||
assert.EqualValues(t, testdata.expect, conf.SortMode)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -81,7 +80,7 @@ func TestPreparePattern(t *testing.T) {
|
||||
var tests = []struct {
|
||||
patterns []*Pattern
|
||||
name string
|
||||
wanterr bool
|
||||
wanterror bool
|
||||
wanticase bool
|
||||
wantneg bool
|
||||
}{
|
||||
@@ -123,16 +122,16 @@ func TestPreparePattern(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testdata := range tests {
|
||||
testname := fmt.Sprintf("PreparePattern-pattern-%s-wanterr-%t", testdata.name, testdata.wanterr)
|
||||
testname := fmt.Sprintf("PreparePattern-pattern-%s-wanterr-%t", testdata.name, testdata.wanterror)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
conf := Config{}
|
||||
|
||||
err := conf.PreparePattern(testdata.patterns)
|
||||
|
||||
if err != nil {
|
||||
if !testdata.wanterr {
|
||||
t.Errorf("PreparePattern returned error: %s", err)
|
||||
}
|
||||
if testdata.wanterror {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
14
cmd/root.go
14
cmd/root.go
@@ -20,6 +20,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -122,7 +123,7 @@ func Execute() {
|
||||
"Use alternating background colors")
|
||||
rootCmd.PersistentFlags().StringVarP(&ShowCompletion, "completion", "", "",
|
||||
"Display completion code")
|
||||
rootCmd.PersistentFlags().StringVarP(&conf.Separator, "separator", "s", cfg.DefaultSeparator,
|
||||
rootCmd.PersistentFlags().StringVarP(&conf.Separator, "separator", "s", cfg.SeparatorTemplates[":default:"],
|
||||
"Custom field separator")
|
||||
rootCmd.PersistentFlags().StringVarP(&conf.Columns, "columns", "c", "",
|
||||
"Only show the speficied columns (separated by ,)")
|
||||
@@ -131,7 +132,11 @@ func Execute() {
|
||||
rootCmd.PersistentFlags().StringVarP(&conf.TransposeColumns, "transpose-columns", "T", "",
|
||||
"Transpose the speficied columns (separated by ,)")
|
||||
rootCmd.PersistentFlags().BoolVarP(&conf.Interactive, "interactive", "I", false,
|
||||
"interactive mode (experimental)")
|
||||
"interactive mode")
|
||||
rootCmd.PersistentFlags().StringVarP(&conf.OFS, "ofs", "", "",
|
||||
"Output field separator (' ' for ascii table, ',' for CSV)")
|
||||
rootCmd.PersistentFlags().BoolVarP(&conf.InputJSON, "json", "j", false,
|
||||
"JSON input mode")
|
||||
|
||||
// sort options
|
||||
rootCmd.PersistentFlags().StringVarP(&conf.SortByColumn, "sort-by", "k", "",
|
||||
@@ -183,6 +188,11 @@ func Execute() {
|
||||
|
||||
rootCmd.SetUsageTemplate(strings.TrimSpace(usage) + "\n")
|
||||
|
||||
if slices.Contains(os.Args, "-h") {
|
||||
fmt.Println(shortusage)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
|
||||
16
cmd/shortusage.go
Normal file
16
cmd/shortusage.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package cmd
|
||||
|
||||
const shortusage = `tablizer [regex,...] [-r file] [flags]
|
||||
-c col,... show specified columns -L highlight matching lines
|
||||
-k col,... sort by specified columns -j read JSON input
|
||||
-F col=reg filter field with regexp -v invert match
|
||||
-T col,... transpose specified columns -n numberize columns
|
||||
-R /from/to/ apply replacement to columns in -T -N do not use colors
|
||||
-y col,... yank columns to clipboard -H do not show headers
|
||||
--ofs char output field separator -s specify field separator
|
||||
-r file read input from file -z use fuzzy search
|
||||
-f file read config from file -I interactive filter mode
|
||||
-d debug
|
||||
-O org -C CSV -M md -X ext -S shell -Y yaml -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`
|
||||
205
cmd/tablizer.go
205
cmd/tablizer.go
@@ -6,47 +6,50 @@ NAME
|
||||
|
||||
SYNOPSIS
|
||||
Usage:
|
||||
tablizer [regex,...] [file, ...] [flags]
|
||||
tablizer [regex,...] [-r file] [flags]
|
||||
|
||||
Operational Flags:
|
||||
-c, --columns string Only show the speficied columns (separated by ,)
|
||||
-v, --invert-match select non-matching rows
|
||||
-n, --numbering Enable header numbering
|
||||
-N, --no-color Disable pattern highlighting
|
||||
-H, --no-headers Disable headers display
|
||||
-s, --separator string Custom field separator
|
||||
-k, --sort-by int|name Sort by column (default: 1)
|
||||
-z, --fuzzy Use fuzzy search [experimental]
|
||||
-F, --filter field[!]=reg Filter given field with regex, can be used multiple times
|
||||
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
|
||||
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T
|
||||
-I, --interactive Interactively filter and select rows
|
||||
-c, --columns string Only show the speficied columns (separated by ,)
|
||||
-v, --invert-match select non-matching rows
|
||||
-n, --numbering Enable header numbering
|
||||
-N, --no-color Disable pattern highlighting
|
||||
-H, --no-headers Disable headers display
|
||||
-s, --separator <string> Custom field separator (maybe char, string or :class:)
|
||||
-k, --sort-by <int|name> Sort by column (default: 1)
|
||||
-z, --fuzzy Use fuzzy search [experimental]
|
||||
-F, --filter <field[!]=reg> Filter given field with regex, can be used multiple times
|
||||
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
|
||||
-R, --regex-transposer </from/to/> Apply /search/replace/ regexp to fields given in -T
|
||||
-j, --json Read JSON input (must be array of hashes)
|
||||
-I, --interactive Interactively filter and select rows
|
||||
|
||||
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 output
|
||||
-Y, --yaml Enable yaml output
|
||||
-C, --csv Enable CSV output
|
||||
-A, --ascii Default output mode, ascii tabular
|
||||
-L, --hightlight-lines Use alternating background colors for tables
|
||||
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
|
||||
space separated
|
||||
-X, --extended Enable extended output
|
||||
-M, --markdown Enable markdown table output
|
||||
-O, --orgtbl Enable org-mode table output
|
||||
-S, --shell Enable shell evaluable output
|
||||
-Y, --yaml Enable yaml output
|
||||
-C, --csv Enable CSV output
|
||||
-A, --ascii Default output mode, ascii tabular
|
||||
-L, --hightlight-lines Use alternating background colors for tables
|
||||
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
|
||||
space separated
|
||||
--ofs <char> Output field separator, used by -A and -C.
|
||||
|
||||
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
|
||||
-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>
|
||||
-f, --config <file> Configuration file (default: ~/.config/tablizer/config)
|
||||
-d, --debug Enable debugging
|
||||
-h, --help help for tablizer
|
||||
-m, --man Display manual page
|
||||
-V, --version Print program version
|
||||
-r --read-file <file> Use <file> as input instead of STDIN
|
||||
--completion <shell> Generate the autocompletion script for <shell>
|
||||
-f, --config <file> Configuration file (default: ~/.config/tablizer/config)
|
||||
-d, --debug Enable debugging
|
||||
-h, --help help for tablizer
|
||||
-m, --man Display manual page
|
||||
-V, --version Print program version
|
||||
|
||||
DESCRIPTION
|
||||
Many programs generate tabular output. But sometimes you need to
|
||||
@@ -74,16 +77,16 @@ DESCRIPTION
|
||||
kubectl get pods | tablizer
|
||||
|
||||
# read a file
|
||||
tablizer filename
|
||||
tablizer -r filename
|
||||
|
||||
# search for pattern in a file (works like grep)
|
||||
tablizer regex filename
|
||||
tablizer regex -r filename
|
||||
|
||||
# search for pattern in STDIN
|
||||
kubectl get pods | tablizer regex
|
||||
|
||||
The output looks like the original one but every header field will have
|
||||
a numer associated with it, e.g.:
|
||||
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.:
|
||||
|
||||
NAME(1) READY(2) STATUS(3) RESTARTS(4) AGE(5)
|
||||
|
||||
@@ -95,7 +98,13 @@ DESCRIPTION
|
||||
You can specify the numbers in any order but output will always follow
|
||||
the original order.
|
||||
|
||||
The numbering can be suppressed by using the -n option.
|
||||
However, you may also just use the header names instead of numbers, eg:
|
||||
|
||||
kubectl get pods | tablizer -cname,status
|
||||
|
||||
You can also use regular expressions with -c, eg:
|
||||
|
||||
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
|
||||
@@ -132,6 +141,57 @@ DESCRIPTION
|
||||
Finally the -d option enables debugging output which is mostly useful
|
||||
for the developer.
|
||||
|
||||
SEPARATOR
|
||||
The option -s can be a single character, in which case the CSV parser
|
||||
will be invoked. You can also specify a string as separator. The string
|
||||
will be interpreted as literal string unless it is a valid go regular
|
||||
expression. For example:
|
||||
|
||||
-s '\t{2,}\'
|
||||
|
||||
is being used as a regexp and will match two or more consecutive tabs.
|
||||
|
||||
-s 'foo'
|
||||
|
||||
on the other hand is no regular expression and will be used literally.
|
||||
|
||||
To make live easier, there are a couple of predefined regular
|
||||
expressions, which you can specify as classes:
|
||||
|
||||
* :tab:
|
||||
|
||||
Matches a tab and eats spaces around it.
|
||||
|
||||
* :spaces:
|
||||
|
||||
Matches 2 or more spaces.
|
||||
|
||||
* :pipe:
|
||||
|
||||
Matches a pipe character and eats spaces around it.
|
||||
|
||||
* :default:
|
||||
|
||||
Matches 2 or more spaces or tab. This is the default separator if
|
||||
none is specified.
|
||||
|
||||
* :nonword:
|
||||
|
||||
Matches a non-word character.
|
||||
|
||||
* :nondigit:
|
||||
|
||||
Matches a non-digit character.
|
||||
|
||||
* :special:
|
||||
|
||||
Matches one or more special chars like brackets, dollar sign,
|
||||
slashes etc.
|
||||
|
||||
* :nonprint:
|
||||
|
||||
Matches one or more non-printable characters.
|
||||
|
||||
PATTERNS AND FILTERING
|
||||
You can reduce the rows being displayed by using one or more regular
|
||||
expression patterns. The regexp language being used is the one of
|
||||
@@ -441,47 +501,50 @@ AUTHORS
|
||||
var usage = `
|
||||
|
||||
Usage:
|
||||
tablizer [regex,...] [file, ...] [flags]
|
||||
tablizer [regex,...] [-r file] [flags]
|
||||
|
||||
Operational Flags:
|
||||
-c, --columns string Only show the speficied columns (separated by ,)
|
||||
-v, --invert-match select non-matching rows
|
||||
-n, --numbering Enable header numbering
|
||||
-N, --no-color Disable pattern highlighting
|
||||
-H, --no-headers Disable headers display
|
||||
-s, --separator string Custom field separator
|
||||
-k, --sort-by int|name Sort by column (default: 1)
|
||||
-z, --fuzzy Use fuzzy search [experimental]
|
||||
-F, --filter field[!]=reg Filter given field with regex, can be used multiple times
|
||||
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
|
||||
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T
|
||||
-I, --interactive Interactively filter and select rows
|
||||
-c, --columns string Only show the speficied columns (separated by ,)
|
||||
-v, --invert-match select non-matching rows
|
||||
-n, --numbering Enable header numbering
|
||||
-N, --no-color Disable pattern highlighting
|
||||
-H, --no-headers Disable headers display
|
||||
-s, --separator <string> Custom field separator (maybe char, string or :class:)
|
||||
-k, --sort-by <int|name> Sort by column (default: 1)
|
||||
-z, --fuzzy Use fuzzy search [experimental]
|
||||
-F, --filter <field[!]=reg> Filter given field with regex, can be used multiple times
|
||||
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
|
||||
-R, --regex-transposer </from/to/> Apply /search/replace/ regexp to fields given in -T
|
||||
-j, --json Read JSON input (must be array of hashes)
|
||||
-I, --interactive Interactively filter and select rows
|
||||
|
||||
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 output
|
||||
-Y, --yaml Enable yaml output
|
||||
-C, --csv Enable CSV output
|
||||
-A, --ascii Default output mode, ascii tabular
|
||||
-L, --hightlight-lines Use alternating background colors for tables
|
||||
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
|
||||
space separated
|
||||
-X, --extended Enable extended output
|
||||
-M, --markdown Enable markdown table output
|
||||
-O, --orgtbl Enable org-mode table output
|
||||
-S, --shell Enable shell evaluable output
|
||||
-Y, --yaml Enable yaml output
|
||||
-C, --csv Enable CSV output
|
||||
-A, --ascii Default output mode, ascii tabular
|
||||
-L, --hightlight-lines Use alternating background colors for tables
|
||||
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
|
||||
space separated
|
||||
--ofs <char> Output field separator, used by -A and -C.
|
||||
|
||||
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
|
||||
-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>
|
||||
-f, --config <file> Configuration file (default: ~/.config/tablizer/config)
|
||||
-d, --debug Enable debugging
|
||||
-h, --help help for tablizer
|
||||
-m, --man Display manual page
|
||||
-V, --version Print program version
|
||||
-r --read-file <file> Use <file> as input instead of STDIN
|
||||
--completion <shell> Generate the autocompletion script for <shell>
|
||||
-f, --config <file> Configuration file (default: ~/.config/tablizer/config)
|
||||
-d, --debug Enable debugging
|
||||
-h, --help help for tablizer
|
||||
-m, --man Display manual page
|
||||
-V, --version Print program version
|
||||
|
||||
|
||||
`
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
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
31
demo/demo.sh
@@ -1,31 +0,0 @@
|
||||
#!/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')
|
||||
@@ -1,4 +0,0 @@
|
||||
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
|
||||
@@ -1,3 +0,0 @@
|
||||
PID TTY TIME CMD
|
||||
30912 pts/0 00:00:00 bash
|
||||
49526 pts/0 00:00:00 ps
|
||||
@@ -1,54 +0,0 @@
|
||||
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
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
{"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"]
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 243 KiB |
25
go.mod
25
go.mod
@@ -1,22 +1,22 @@
|
||||
module github.com/tlinden/tablizer
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.5
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/alecthomas/repr v0.5.1
|
||||
github.com/alecthomas/repr v0.5.2
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
|
||||
github.com/charmbracelet/bubbles v0.21.0
|
||||
github.com/charmbracelet/bubbletea v1.3.6
|
||||
github.com/charmbracelet/bubbletea v1.3.10
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/evertras/bubble-table v0.19.0
|
||||
github.com/evertras/bubble-table v0.19.2
|
||||
github.com/gookit/color v1.6.0
|
||||
github.com/hashicorp/hcl/v2 v2.24.0
|
||||
github.com/lithammer/fuzzysearch v1.1.8
|
||||
github.com/olekukonko/tablewriter v1.0.9
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/olekukonko/tablewriter v1.1.0
|
||||
github.com/rogpeppe/go-internal v1.14.1
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tiagomelo/go-clipboard v0.1.2
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -27,16 +27,16 @@ require (
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.3.1 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.9.3 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.10.1 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
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/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-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
@@ -47,14 +47,15 @@ require (
|
||||
github.com/olekukonko/errors v1.1.0 // indirect
|
||||
github.com/olekukonko/ll v0.0.9 // indirect
|
||||
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/spf13/pflag v1.0.6 // 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/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
)
|
||||
|
||||
36
go.sum
36
go.sum
@@ -1,7 +1,7 @@
|
||||
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.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
||||
github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
||||
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
|
||||
@@ -12,14 +12,14 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
|
||||
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
|
||||
github.com/charmbracelet/bubbletea v1.3.6 h1:VkHIxPJQeDt0aFJIsVxw8BQdh/F/L2KKZGsK6et5taU=
|
||||
github.com/charmbracelet/bubbletea v1.3.6/go.mod h1:oQD9VCRQFF8KplacJLo28/jofOI2ToOfGYeFgBBxHOc=
|
||||
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
|
||||
github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/x/ansi v0.9.3 h1:BXt5DHS/MKF+LjuK4huWrC6NCvHtexww7dMayh6GXd0=
|
||||
github.com/charmbracelet/x/ansi v0.9.3/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
||||
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
|
||||
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
@@ -30,8 +30,8 @@ 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/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/evertras/bubble-table v0.19.0 h1:+JlXRUjNuBN1JI7XU1PapmW1wglbcqZUKkiPnVKPgrc=
|
||||
github.com/evertras/bubble-table v0.19.0/go.mod h1:ifHujS1YxwnYSOgcR2+m3GnJ84f7CVU/4kUOxUCjEbQ=
|
||||
github.com/evertras/bubble-table v0.19.2 h1:u77oiM6JlRR+CvS5FZc3Hz+J6iEsvEDcR5kO8OFb1Yw=
|
||||
github.com/evertras/bubble-table v0.19.2/go.mod h1:ifHujS1YxwnYSOgcR2+m3GnJ84f7CVU/4kUOxUCjEbQ=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
@@ -74,8 +74,8 @@ github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5
|
||||
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
|
||||
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
|
||||
github.com/olekukonko/tablewriter v1.0.9 h1:XGwRsYLC2bY7bNd93Dk51bcPZksWZmLYuaTHR0FqfL8=
|
||||
github.com/olekukonko/tablewriter v1.0.9/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
|
||||
github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY=
|
||||
github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@@ -88,14 +88,14 @@ 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/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
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=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tiagomelo/go-clipboard v0.1.2 h1:Ph2icR0vZRIj3v5ExvsGweBwsbbDUTlS6HoF40MkQD8=
|
||||
github.com/tiagomelo/go-clipboard v0.1.2/go.mod h1:kXtjJBIMimZaGbxmcKZ8+JqK+acSNf5tAJiChlZBOr8=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
@@ -130,8 +130,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
|
||||
@@ -19,9 +19,9 @@ package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tlinden/tablizer/cfg"
|
||||
)
|
||||
|
||||
@@ -56,13 +56,11 @@ func TestMatchPattern(t *testing.T) {
|
||||
}
|
||||
|
||||
err := conf.PreparePattern(inputdata.patterns)
|
||||
if err != nil {
|
||||
t.Errorf("PreparePattern returned error: %s", err)
|
||||
}
|
||||
|
||||
if !matchPattern(conf, inputdata.line) {
|
||||
t.Errorf("matchPattern() did not match\nExp: true\nGot: false\n")
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
res := matchPattern(conf, inputdata.line)
|
||||
assert.EqualValues(t, true, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -163,14 +161,12 @@ func TestFilterByFields(t *testing.T) {
|
||||
conf := cfg.Config{Rawfilters: inputdata.filter, InvertMatch: inputdata.invert}
|
||||
|
||||
err := conf.PrepareFilters()
|
||||
if err != nil {
|
||||
t.Errorf("PrepareFilters returned error: %s", err)
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
data, _, _ := FilterByFields(conf, &data)
|
||||
if !reflect.DeepEqual(*data, inputdata.expect) {
|
||||
t.Errorf("Filtered data does not match expected data:\ngot: %+v\nexp: %+v", data, inputdata.expect)
|
||||
}
|
||||
|
||||
assert.EqualValues(t, inputdata.expect, *data)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -30,16 +30,6 @@ import (
|
||||
"github.com/tlinden/tablizer/cfg"
|
||||
)
|
||||
|
||||
func contains(s []int, e int) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func findindex(s []int, e int) (int, bool) {
|
||||
for i, a := range s {
|
||||
if a == e {
|
||||
@@ -172,48 +162,32 @@ func PrepareColumnVars(columns string, data *Tabdata) ([]int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// deduplicate: put all values into a map (value gets map key)
|
||||
// thereby removing duplicates, extract keys into new slice
|
||||
// and sort it
|
||||
imap := make(map[int]int, len(usecolumns))
|
||||
// deduplicate columns, preserve order
|
||||
deduped := []int{}
|
||||
for _, i := range usecolumns {
|
||||
imap[i] = 0
|
||||
if !slices.Contains(deduped, i) {
|
||||
deduped = append(deduped, i)
|
||||
}
|
||||
}
|
||||
|
||||
// fill with deduplicated columns
|
||||
usecolumns = nil
|
||||
|
||||
for k := range imap {
|
||||
usecolumns = append(usecolumns, k)
|
||||
}
|
||||
|
||||
sort.Ints(usecolumns)
|
||||
|
||||
return usecolumns, nil
|
||||
return deduped, nil
|
||||
}
|
||||
|
||||
// prepare headers: add numbers to headers
|
||||
func numberizeAndReduceHeaders(conf cfg.Config, data *Tabdata) {
|
||||
numberedHeaders := []string{}
|
||||
numberedHeaders := make([]string, len(data.headers))
|
||||
|
||||
maxwidth := 0 // start from scratch, so we only look at displayed column widths
|
||||
|
||||
// add numbers to headers if needed, get widest cell width
|
||||
for idx, head := range data.headers {
|
||||
var headlen int
|
||||
|
||||
if len(conf.Columns) > 0 {
|
||||
// -c specified
|
||||
if !contains(conf.UseColumns, idx+1) {
|
||||
// ignore this one
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if conf.Numbering {
|
||||
numhead := fmt.Sprintf("%s(%d)", head, idx+1)
|
||||
headlen = len(numhead)
|
||||
numberedHeaders = append(numberedHeaders, numhead)
|
||||
newhead := fmt.Sprintf("%s(%d)", head, idx+1)
|
||||
numberedHeaders[idx] = newhead
|
||||
headlen = len(newhead)
|
||||
} else {
|
||||
numberedHeaders = append(numberedHeaders, head)
|
||||
headlen = len(head)
|
||||
}
|
||||
|
||||
@@ -222,7 +196,24 @@ func numberizeAndReduceHeaders(conf cfg.Config, data *Tabdata) {
|
||||
}
|
||||
}
|
||||
|
||||
data.headers = numberedHeaders
|
||||
if conf.Numbering {
|
||||
data.headers = numberedHeaders
|
||||
}
|
||||
|
||||
if len(conf.UseColumns) > 0 {
|
||||
// re-align headers based on user requested column list
|
||||
headers := make([]string, len(conf.UseColumns))
|
||||
|
||||
for i, col := range conf.UseColumns {
|
||||
for idx := range data.headers {
|
||||
if col-1 == idx {
|
||||
headers[i] = data.headers[col-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.headers = headers
|
||||
}
|
||||
|
||||
if data.maxwidthHeader != maxwidth && maxwidth > 0 {
|
||||
data.maxwidthHeader = maxwidth
|
||||
@@ -234,17 +225,17 @@ func reduceColumns(conf cfg.Config, data *Tabdata) {
|
||||
if len(conf.Columns) > 0 {
|
||||
reducedEntries := [][]string{}
|
||||
|
||||
var reducedEntry []string
|
||||
|
||||
for _, entry := range data.entries {
|
||||
reducedEntry = nil
|
||||
var reducedEntry []string
|
||||
|
||||
for i, value := range entry {
|
||||
if !contains(conf.UseColumns, i+1) {
|
||||
continue
|
||||
for _, col := range conf.UseColumns {
|
||||
col--
|
||||
|
||||
for idx, value := range entry {
|
||||
if idx == col {
|
||||
reducedEntry = append(reducedEntry, value)
|
||||
}
|
||||
}
|
||||
|
||||
reducedEntry = append(reducedEntry, value)
|
||||
}
|
||||
|
||||
reducedEntries = append(reducedEntries, reducedEntry)
|
||||
|
||||
@@ -19,9 +19,10 @@ package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tlinden/tablizer/cfg"
|
||||
)
|
||||
|
||||
@@ -38,10 +39,9 @@ func TestContains(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
testname := fmt.Sprintf("contains-%d,%d,%t", tt.list, tt.search, tt.want)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
answer := contains(tt.list, tt.search)
|
||||
if answer != tt.want {
|
||||
t.Errorf("got %t, want %t", answer, tt.want)
|
||||
}
|
||||
answer := slices.Contains(tt.list, tt.search)
|
||||
|
||||
assert.EqualValues(t, tt.want, answer)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -73,18 +73,17 @@ func TestPrepareColumns(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testdata := range tests {
|
||||
testname := fmt.Sprintf("PrepareColumns-%s-%t", testdata.input, testdata.wanterror)
|
||||
testname := fmt.Sprintf("PrepareColumns-%s-%t",
|
||||
testdata.input, testdata.wanterror)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
conf := cfg.Config{Columns: testdata.input}
|
||||
err := PrepareColumns(&conf, &data)
|
||||
if err != nil {
|
||||
if !testdata.wanterror {
|
||||
t.Errorf("got error: %v", err)
|
||||
}
|
||||
|
||||
if testdata.wanterror {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
if !reflect.DeepEqual(conf.UseColumns, testdata.exp) {
|
||||
t.Errorf("got: %v, expected: %v", conf.UseColumns, testdata.exp)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, testdata.exp, conf.UseColumns)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -153,18 +152,13 @@ func TestPrepareTransposerColumns(t *testing.T) {
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
conf := cfg.Config{TransposeColumns: testdata.input, Transposers: testdata.transp}
|
||||
err := PrepareTransposerColumns(&conf, &data)
|
||||
if err != nil {
|
||||
if !testdata.wanterror {
|
||||
t.Errorf("got error: %v", err)
|
||||
}
|
||||
} else {
|
||||
if len(conf.UseTransposeColumns) != testdata.exp {
|
||||
t.Errorf("got %d, want %d", conf.UseTransposeColumns, testdata.exp)
|
||||
}
|
||||
|
||||
if len(conf.Transposers) != len(conf.UseTransposeColumns) {
|
||||
t.Errorf("got %d, want %d", conf.UseTransposeColumns, testdata.exp)
|
||||
}
|
||||
if testdata.wanterror {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, testdata.exp, len(conf.UseTransposeColumns))
|
||||
assert.EqualValues(t, len(conf.UseTransposeColumns), len(conf.Transposers))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -202,10 +196,8 @@ func TestReduceColumns(t *testing.T) {
|
||||
c := cfg.Config{Columns: "x", UseColumns: testdata.columns}
|
||||
data := Tabdata{entries: input}
|
||||
reduceColumns(c, &data)
|
||||
if !reflect.DeepEqual(data.entries, testdata.expect) {
|
||||
t.Errorf("reduceColumns returned invalid data:\ngot: %+v\nexp: %+v",
|
||||
data.entries, testdata.expect)
|
||||
}
|
||||
|
||||
assert.EqualValues(t, testdata.expect, data.entries)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -233,10 +225,8 @@ func TestNumberizeHeaders(t *testing.T) {
|
||||
conf := cfg.Config{Columns: "x", UseColumns: testdata.columns, Numbering: testdata.numberize}
|
||||
usedata := data
|
||||
numberizeAndReduceHeaders(conf, &usedata)
|
||||
if !reflect.DeepEqual(usedata.headers, testdata.expect) {
|
||||
t.Errorf("numberizeAndReduceHeaders returned invalid data:\ngot: %+v\nexp: %+v",
|
||||
usedata.headers, testdata.expect)
|
||||
}
|
||||
|
||||
assert.EqualValues(t, testdata.expect, usedata.headers)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
139
lib/parser.go
139
lib/parser.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
|
||||
@@ -20,8 +20,12 @@ package lib
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@@ -39,6 +43,8 @@ func Parse(conf cfg.Config, input io.Reader) (Tabdata, error) {
|
||||
// first step, parse the data
|
||||
if len(conf.Separator) == 1 {
|
||||
data, err = parseCSV(conf, input)
|
||||
} else if conf.InputJSON {
|
||||
data, err = parseJSON(conf, input)
|
||||
} else {
|
||||
data, err = parseTabular(conf, input)
|
||||
}
|
||||
@@ -172,6 +178,137 @@ func parseTabular(conf cfg.Config, input io.Reader) (Tabdata, error) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Parse JSON input. We only support an array of maps.
|
||||
*/
|
||||
func parseRawJSON(conf cfg.Config, input io.Reader) (Tabdata, error) {
|
||||
dec := json.NewDecoder(input)
|
||||
headers := []string{}
|
||||
idxmap := map[string]int{}
|
||||
data := [][]string{}
|
||||
row := []string{}
|
||||
iskey := true
|
||||
haveheaders := false
|
||||
var currentfield string
|
||||
var idx int
|
||||
var isjson bool
|
||||
|
||||
for {
|
||||
t, err := dec.Token()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
switch val := t.(type) {
|
||||
case string:
|
||||
if iskey {
|
||||
if !haveheaders {
|
||||
// consider only the keys of the first item as headers
|
||||
headers = append(headers, val)
|
||||
}
|
||||
currentfield = val
|
||||
} else {
|
||||
if !haveheaders {
|
||||
// the first row uses the order as it comes in
|
||||
row = append(row, val)
|
||||
} else {
|
||||
// use the pre-determined order, that way items
|
||||
// can be in any order as long as they contain all
|
||||
// neccessary fields. They may also contain less
|
||||
// fields than the first item, these will contain
|
||||
// the empty string
|
||||
row[idxmap[currentfield]] = val
|
||||
}
|
||||
}
|
||||
|
||||
case float64:
|
||||
var value string
|
||||
|
||||
// we set precision to 0 if the float is a whole number
|
||||
if val == math.Trunc(val) {
|
||||
value = fmt.Sprintf("%.f", val)
|
||||
} else {
|
||||
value = fmt.Sprintf("%f", val)
|
||||
}
|
||||
|
||||
if !haveheaders {
|
||||
row = append(row, value)
|
||||
} else {
|
||||
row[idxmap[currentfield]] = value
|
||||
}
|
||||
|
||||
case nil:
|
||||
// we ignore here if a value shall be an int or a string,
|
||||
// because tablizer only works with strings anyway
|
||||
if !haveheaders {
|
||||
row = append(row, "")
|
||||
} else {
|
||||
row[idxmap[currentfield]] = ""
|
||||
}
|
||||
|
||||
case json.Delim:
|
||||
if val.String() == "}" {
|
||||
data = append(data, row)
|
||||
row = make([]string, len(headers))
|
||||
idx++
|
||||
|
||||
if !haveheaders {
|
||||
// remember the array position of header fields,
|
||||
// which we use to assign elements to the correct
|
||||
// row index
|
||||
for i, header := range headers {
|
||||
idxmap[header] = i
|
||||
}
|
||||
}
|
||||
|
||||
haveheaders = true
|
||||
}
|
||||
isjson = true
|
||||
default:
|
||||
fmt.Printf("unknown token: %v type: %T\n", t, t)
|
||||
}
|
||||
|
||||
iskey = !iskey
|
||||
}
|
||||
|
||||
if isjson && (len(headers) == 0 || len(data) == 0) {
|
||||
return Tabdata{}, errors.New("failed to parse JSON, input did not contain array of hashes")
|
||||
}
|
||||
|
||||
return Tabdata{headers: headers, entries: data, columns: len(headers)}, nil
|
||||
}
|
||||
|
||||
func parseJSON(conf cfg.Config, input io.Reader) (Tabdata, error) {
|
||||
// parse raw json
|
||||
data, err := parseRawJSON(conf, input)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
// apply filter, if any
|
||||
filtered := [][]string{}
|
||||
var line string
|
||||
|
||||
for _, row := range data.entries {
|
||||
line = strings.Join(row, " ")
|
||||
|
||||
if matchPattern(conf, line) == conf.InvertMatch {
|
||||
continue
|
||||
}
|
||||
|
||||
filtered = append(filtered, row)
|
||||
}
|
||||
|
||||
if len(filtered) != len(data.entries) {
|
||||
data.entries = filtered
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func PostProcess(conf cfg.Config, data *Tabdata) (*Tabdata, bool, error) {
|
||||
var modified bool
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright © 2022 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
|
||||
@@ -19,10 +19,11 @@ package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tlinden/tablizer/cfg"
|
||||
)
|
||||
|
||||
@@ -33,7 +34,7 @@ var input = []struct {
|
||||
}{
|
||||
{
|
||||
name: "tabular-data",
|
||||
separator: cfg.DefaultSeparator,
|
||||
separator: cfg.SeparatorTemplates[":default:"],
|
||||
text: `
|
||||
ONE TWO THREE
|
||||
asd igig cxxxncnc
|
||||
@@ -67,27 +68,21 @@ func TestParser(t *testing.T) {
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
readFd := strings.NewReader(strings.TrimSpace(testdata.text))
|
||||
conf := cfg.Config{Separator: testdata.separator}
|
||||
gotdata, err := Parse(conf, readFd)
|
||||
gotdata, err := wrapValidateParser(conf, readFd)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(data, gotdata) {
|
||||
t.Errorf("Parser returned invalid data\nExp: %+v\nGot: %+v\n",
|
||||
data, gotdata)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, data, gotdata)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParserPatternmatching(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
entries [][]string
|
||||
patterns []*cfg.Pattern
|
||||
invert bool
|
||||
want bool
|
||||
name string
|
||||
entries [][]string
|
||||
patterns []*cfg.Pattern
|
||||
invert bool
|
||||
wanterror bool
|
||||
}{
|
||||
{
|
||||
name: "match",
|
||||
@@ -121,18 +116,13 @@ func TestParserPatternmatching(t *testing.T) {
|
||||
_ = conf.PreparePattern(testdata.patterns)
|
||||
|
||||
readFd := strings.NewReader(strings.TrimSpace(inputdata.text))
|
||||
gotdata, err := Parse(conf, readFd)
|
||||
data, err := wrapValidateParser(conf, readFd)
|
||||
|
||||
if err != nil {
|
||||
if !testdata.want {
|
||||
t.Errorf("Parser returned error: %s\nData processed so far: %+v",
|
||||
err, gotdata)
|
||||
}
|
||||
if testdata.wanterror {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
if !reflect.DeepEqual(testdata.entries, gotdata.entries) {
|
||||
t.Errorf("Parser returned invalid data (pattern: %s, invert: %t)\nExp: %+v\nGot: %+v\n",
|
||||
testdata.name, testdata.invert, testdata.entries, gotdata.entries)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, testdata.entries, data.entries)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -158,15 +148,232 @@ asd igig
|
||||
19191 EDD 1 X`
|
||||
|
||||
readFd := strings.NewReader(strings.TrimSpace(table))
|
||||
conf := cfg.Config{Separator: cfg.DefaultSeparator}
|
||||
gotdata, err := Parse(conf, readFd)
|
||||
conf := cfg.Config{Separator: cfg.SeparatorTemplates[":default:"]}
|
||||
gotdata, err := wrapValidateParser(conf, readFd)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, data, gotdata)
|
||||
}
|
||||
|
||||
func TestParserJSONInput(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
input string
|
||||
expect Tabdata
|
||||
wanterror bool // true: expect fail, false: expect success
|
||||
}{
|
||||
{
|
||||
// too deep nesting
|
||||
name: "invalidjson",
|
||||
wanterror: true,
|
||||
input: `[
|
||||
{
|
||||
"item": {
|
||||
"NAME": "postgres-operator-7f4c7c8485-ntlns",
|
||||
"READY": "1/1",
|
||||
"STATUS": "Running",
|
||||
"RESTARTS": "0",
|
||||
"AGE": "24h"
|
||||
}
|
||||
}
|
||||
`,
|
||||
expect: Tabdata{},
|
||||
},
|
||||
|
||||
{
|
||||
// contains nil, int and float values
|
||||
name: "niljson",
|
||||
wanterror: false,
|
||||
input: `[
|
||||
{
|
||||
"NAME": "postgres-operator-7f4c7c8485-ntlns",
|
||||
"READY": "1/1",
|
||||
"STATUS": "Running",
|
||||
"RESTARTS": 0,
|
||||
"AGE": null,
|
||||
"X": 12,
|
||||
"Y": 34.222
|
||||
}
|
||||
]`,
|
||||
expect: Tabdata{
|
||||
columns: 7,
|
||||
headers: []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE", "X", "Y"},
|
||||
entries: [][]string{
|
||||
[]string{
|
||||
"postgres-operator-7f4c7c8485-ntlns",
|
||||
"1/1",
|
||||
"Running",
|
||||
"0",
|
||||
"",
|
||||
"12",
|
||||
"34.222000",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
// one field missing + different order
|
||||
// but shall not fail
|
||||
name: "kgpfail",
|
||||
wanterror: false,
|
||||
input: `[
|
||||
{
|
||||
"NAME": "postgres-operator-7f4c7c8485-ntlns",
|
||||
"READY": "1/1",
|
||||
"STATUS": "Running",
|
||||
"RESTARTS": "0",
|
||||
"AGE": "24h"
|
||||
},
|
||||
{
|
||||
"NAME": "wal-g-exporter-778dcd95f5-wcjzn",
|
||||
"RESTARTS": "0",
|
||||
"READY": "1/1",
|
||||
"AGE": "24h"
|
||||
}
|
||||
]`,
|
||||
expect: Tabdata{
|
||||
columns: 5,
|
||||
headers: []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"},
|
||||
entries: [][]string{
|
||||
[]string{
|
||||
"postgres-operator-7f4c7c8485-ntlns",
|
||||
"1/1",
|
||||
"Running",
|
||||
"0",
|
||||
"24h",
|
||||
},
|
||||
[]string{
|
||||
"wal-g-exporter-778dcd95f5-wcjzn",
|
||||
"1/1",
|
||||
"",
|
||||
"0",
|
||||
"24h",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "kgp",
|
||||
wanterror: false,
|
||||
input: `[
|
||||
{
|
||||
"NAME": "postgres-operator-7f4c7c8485-ntlns",
|
||||
"READY": "1/1",
|
||||
"STATUS": "Running",
|
||||
"RESTARTS": "0",
|
||||
"AGE": "24h"
|
||||
},
|
||||
{
|
||||
"NAME": "wal-g-exporter-778dcd95f5-wcjzn",
|
||||
"STATUS": "Running",
|
||||
"READY": "1/1",
|
||||
"RESTARTS": "0",
|
||||
"AGE": "24h"
|
||||
}
|
||||
]`,
|
||||
expect: Tabdata{
|
||||
columns: 5,
|
||||
headers: []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"},
|
||||
entries: [][]string{
|
||||
[]string{
|
||||
"postgres-operator-7f4c7c8485-ntlns",
|
||||
"1/1",
|
||||
"Running",
|
||||
"0",
|
||||
"24h",
|
||||
},
|
||||
[]string{
|
||||
"wal-g-exporter-778dcd95f5-wcjzn",
|
||||
"1/1",
|
||||
"Running",
|
||||
"0",
|
||||
"24h",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(data, gotdata) {
|
||||
t.Errorf("Parser returned invalid data, Regex: %s\nExp: %+v\nGot: %+v\n",
|
||||
conf.Separator, data, gotdata)
|
||||
for _, testdata := range tests {
|
||||
testname := fmt.Sprintf("parse-json-%s", testdata.name)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
conf := cfg.Config{InputJSON: true}
|
||||
|
||||
readFd := strings.NewReader(strings.TrimSpace(testdata.input))
|
||||
data, err := wrapValidateParser(conf, readFd)
|
||||
|
||||
if testdata.wanterror {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, testdata.expect, data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParserSeparators(t *testing.T) {
|
||||
list := []string{"alpha", "beta", "delta"}
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
sep string
|
||||
}{
|
||||
{
|
||||
input: `🎲`,
|
||||
sep: ":nonprint:",
|
||||
},
|
||||
{
|
||||
input: `|`,
|
||||
sep: ":pipe:",
|
||||
},
|
||||
{
|
||||
input: ` `,
|
||||
sep: ":spaces:",
|
||||
},
|
||||
{
|
||||
input: " \t ",
|
||||
sep: ":tab:",
|
||||
},
|
||||
{
|
||||
input: `-`,
|
||||
sep: ":nonword:",
|
||||
},
|
||||
{
|
||||
input: `//$`,
|
||||
sep: ":special:",
|
||||
},
|
||||
}
|
||||
|
||||
for _, testdata := range tests {
|
||||
testname := fmt.Sprintf("parse-%s", testdata.sep)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
header := strings.Join(list, testdata.input)
|
||||
row := header
|
||||
content := header + "\n" + row
|
||||
|
||||
readFd := strings.NewReader(strings.TrimSpace(content))
|
||||
conf := cfg.Config{Separator: testdata.sep}
|
||||
conf.ApplyDefaults()
|
||||
|
||||
gotdata, err := wrapValidateParser(conf, readFd)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, [][]string{list}, gotdata.entries)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func wrapValidateParser(conf cfg.Config, input io.Reader) (Tabdata, error) {
|
||||
data, err := Parse(conf, input)
|
||||
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
err = ValidateConsistency(&data)
|
||||
|
||||
return data, err
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func printData(writer io.Writer, conf cfg.Config, data *Tabdata) {
|
||||
case cfg.Yaml:
|
||||
printYamlData(writer, data)
|
||||
case cfg.CSV:
|
||||
printCSVData(writer, data)
|
||||
printCSVData(writer, conf, data)
|
||||
default:
|
||||
printASCIIData(writer, conf, data)
|
||||
}
|
||||
@@ -194,6 +194,11 @@ func printMarkdownData(writer io.Writer, conf cfg.Config, data *Tabdata) {
|
||||
Simple ASCII table without any borders etc, just like the input we expect
|
||||
*/
|
||||
func printASCIIData(writer io.Writer, conf cfg.Config, data *Tabdata) {
|
||||
OFS := " "
|
||||
if conf.OFS != "" {
|
||||
OFS = conf.OFS
|
||||
}
|
||||
|
||||
tableString := &strings.Builder{}
|
||||
|
||||
styleTSV := tw.NewSymbolCustom("space").WithColumn("\t")
|
||||
@@ -204,8 +209,8 @@ func printASCIIData(writer io.Writer, conf cfg.Config, data *Tabdata) {
|
||||
Borders: tw.BorderNone,
|
||||
Symbols: styleTSV,
|
||||
Settings: tw.Settings{
|
||||
Separators: tw.Separators{BetweenRows: tw.Off, BetweenColumns: tw.On},
|
||||
Lines: tw.Lines{ShowFooterLine: tw.Off, ShowHeaderLine: tw.Off},
|
||||
Separators: tw.SeparatorsNone,
|
||||
Lines: tw.LinesNone,
|
||||
},
|
||||
})),
|
||||
tablewriter.WithConfig(tablewriter.Config{
|
||||
@@ -213,23 +218,18 @@ func printASCIIData(writer io.Writer, conf cfg.Config, data *Tabdata) {
|
||||
Formatting: tw.CellFormatting{
|
||||
AutoFormat: tw.Off,
|
||||
},
|
||||
Padding: tw.CellPadding{
|
||||
Global: tw.Padding{Left: "", Right: ""},
|
||||
},
|
||||
Padding: tw.CellPadding{Global: tw.Padding{Left: "", Right: OFS}},
|
||||
},
|
||||
Row: tw.CellConfig{
|
||||
Formatting: tw.CellFormatting{
|
||||
AutoWrap: tw.WrapNone,
|
||||
Alignment: tw.AlignLeft,
|
||||
},
|
||||
Padding: tw.CellPadding{
|
||||
Global: tw.Padding{Left: "", Right: ""},
|
||||
},
|
||||
Padding: tw.CellPadding{Global: tw.Padding{Right: OFS}},
|
||||
},
|
||||
|
||||
Debug: true,
|
||||
}),
|
||||
tablewriter.WithPadding(tw.PaddingNone),
|
||||
)
|
||||
|
||||
if !conf.NoHeaders {
|
||||
@@ -328,8 +328,14 @@ func printYamlData(writer io.Writer, data *Tabdata) {
|
||||
output(writer, string(yamlstr))
|
||||
}
|
||||
|
||||
func printCSVData(writer io.Writer, data *Tabdata) {
|
||||
func printCSVData(writer io.Writer, conf cfg.Config, data *Tabdata) {
|
||||
OFS := ","
|
||||
if conf.OFS != "" {
|
||||
OFS = conf.OFS
|
||||
}
|
||||
|
||||
csvout := csv.NewWriter(writer)
|
||||
csvout.Comma = []rune(OFS)[0]
|
||||
|
||||
if err := csvout.Write(data.headers); err != nil {
|
||||
log.Fatalln("error writing record to csv:", err)
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tlinden/tablizer/cfg"
|
||||
)
|
||||
|
||||
@@ -77,10 +78,10 @@ var tests = []struct {
|
||||
numberize: true,
|
||||
name: "default",
|
||||
expect: `
|
||||
NAME(1) DURATION(2) COUNT(3) WHEN(4)
|
||||
beta 1d10h5m1s 33 3/1/2014
|
||||
alpha 4h35m 170 2013-Feb-03
|
||||
ceta 33d12h 9 06/Jan/2008 15:04:05 -0700`,
|
||||
NAME(1) DURATION(2) COUNT(3) WHEN(4)
|
||||
beta 1d10h5m1s 33 3/1/2014
|
||||
alpha 4h35m 170 2013-Feb-03
|
||||
ceta 33d12h 9 06/Jan/2008 15:04:05 -0700`,
|
||||
},
|
||||
{
|
||||
mode: cfg.CSV,
|
||||
@@ -173,10 +174,10 @@ DURATION(2): 33d12h
|
||||
numberize: true,
|
||||
desc: false,
|
||||
expect: `
|
||||
NAME(1) DURATION(2) COUNT(3) WHEN(4)
|
||||
ceta 33d12h 9 06/Jan/2008 15:04:05 -0700
|
||||
beta 1d10h5m1s 33 3/1/2014
|
||||
alpha 4h35m 170 2013-Feb-03`,
|
||||
NAME(1) DURATION(2) COUNT(3) WHEN(4)
|
||||
ceta 33d12h 9 06/Jan/2008 15:04:05 -0700
|
||||
beta 1d10h5m1s 33 3/1/2014
|
||||
alpha 4h35m 170 2013-Feb-03`,
|
||||
},
|
||||
{
|
||||
name: "sortbycolumn4",
|
||||
@@ -185,10 +186,10 @@ alpha 4h35m 170 2013-Feb-03`,
|
||||
desc: false,
|
||||
numberize: true,
|
||||
expect: `
|
||||
NAME(1) DURATION(2) COUNT(3) WHEN(4)
|
||||
ceta 33d12h 9 06/Jan/2008 15:04:05 -0700
|
||||
alpha 4h35m 170 2013-Feb-03
|
||||
beta 1d10h5m1s 33 3/1/2014`,
|
||||
NAME(1) DURATION(2) COUNT(3) WHEN(4)
|
||||
ceta 33d12h 9 06/Jan/2008 15:04:05 -0700
|
||||
alpha 4h35m 170 2013-Feb-03
|
||||
beta 1d10h5m1s 33 3/1/2014`,
|
||||
},
|
||||
{
|
||||
name: "sortbycolumn2",
|
||||
@@ -197,10 +198,10 @@ beta 1d10h5m1s 33 3/1/2014`,
|
||||
numberize: true,
|
||||
desc: false,
|
||||
expect: `
|
||||
NAME(1) DURATION(2) COUNT(3) WHEN(4)
|
||||
alpha 4h35m 170 2013-Feb-03
|
||||
beta 1d10h5m1s 33 3/1/2014
|
||||
ceta 33d12h 9 06/Jan/2008 15:04:05 -0700`,
|
||||
NAME(1) DURATION(2) COUNT(3) WHEN(4)
|
||||
alpha 4h35m 170 2013-Feb-03
|
||||
beta 1d10h5m1s 33 3/1/2014
|
||||
ceta 33d12h 9 06/Jan/2008 15:04:05 -0700`,
|
||||
},
|
||||
|
||||
// ----------------------- UseColumns Tests
|
||||
@@ -210,44 +211,44 @@ ceta 33d12h 9 06/Jan/2008 15:04:05 -0700`,
|
||||
numberize: true,
|
||||
usecolstr: "1,4",
|
||||
expect: `
|
||||
NAME(1) WHEN(4)
|
||||
beta 3/1/2014
|
||||
alpha 2013-Feb-03
|
||||
ceta 06/Jan/2008 15:04:05 -0700`,
|
||||
NAME(1) WHEN(4)
|
||||
beta 3/1/2014
|
||||
alpha 2013-Feb-03
|
||||
ceta 06/Jan/2008 15:04:05 -0700`,
|
||||
},
|
||||
{
|
||||
name: "usecolumns",
|
||||
name: "usecolumns2",
|
||||
usecol: []int{2},
|
||||
numberize: true,
|
||||
usecolstr: "2",
|
||||
expect: `
|
||||
DURATION(2)
|
||||
1d10h5m1s
|
||||
4h35m
|
||||
DURATION(2)
|
||||
1d10h5m1s
|
||||
4h35m
|
||||
33d12h`,
|
||||
},
|
||||
{
|
||||
name: "usecolumns",
|
||||
name: "usecolumns3",
|
||||
usecol: []int{3},
|
||||
numberize: true,
|
||||
usecolstr: "3",
|
||||
expect: `
|
||||
COUNT(3)
|
||||
33
|
||||
170
|
||||
COUNT(3)
|
||||
33
|
||||
170
|
||||
9`,
|
||||
},
|
||||
{
|
||||
name: "usecolumns",
|
||||
name: "usecolumns4",
|
||||
column: 0,
|
||||
usecol: []int{1, 3},
|
||||
numberize: true,
|
||||
usecolstr: "1,3",
|
||||
expect: `
|
||||
NAME(1) COUNT(3)
|
||||
beta 33
|
||||
alpha 170
|
||||
ceta 9`,
|
||||
NAME(1) COUNT(3)
|
||||
beta 33
|
||||
alpha 170
|
||||
ceta 9`,
|
||||
},
|
||||
{
|
||||
name: "usecolumns",
|
||||
@@ -255,10 +256,10 @@ ceta 9`,
|
||||
numberize: true,
|
||||
usecolstr: "2,4",
|
||||
expect: `
|
||||
DURATION(2) WHEN(4)
|
||||
1d10h5m1s 3/1/2014
|
||||
4h35m 2013-Feb-03
|
||||
33d12h 06/Jan/2008 15:04:05 -0700`,
|
||||
DURATION(2) WHEN(4)
|
||||
1d10h5m1s 3/1/2014
|
||||
4h35m 2013-Feb-03
|
||||
33d12h 06/Jan/2008 15:04:05 -0700`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -280,12 +281,18 @@ func TestPrinter(t *testing.T) {
|
||||
Numbering: testdata.numberize,
|
||||
UseColumns: testdata.usecol,
|
||||
NoColor: true,
|
||||
OFS: " ",
|
||||
}
|
||||
|
||||
if conf.OutputMode == cfg.CSV {
|
||||
conf.OFS = ","
|
||||
}
|
||||
|
||||
if testdata.column > 0 {
|
||||
conf.UseSortByColumn = []int{testdata.column}
|
||||
}
|
||||
|
||||
conf.Separator = cfg.SeparatorTemplates[":default:"]
|
||||
conf.ApplyDefaults()
|
||||
|
||||
// the test checks the len!
|
||||
@@ -302,10 +309,7 @@ func TestPrinter(t *testing.T) {
|
||||
|
||||
got := strings.TrimSpace(writer.String())
|
||||
|
||||
if got != exp {
|
||||
t.Errorf("not rendered correctly:\n+++ got:\n%s\n+++ want:\n%s",
|
||||
got, exp)
|
||||
}
|
||||
assert.EqualValues(t, exp, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tlinden/tablizer/cfg"
|
||||
)
|
||||
|
||||
@@ -41,9 +42,7 @@ func TestDuration2Seconds(t *testing.T) {
|
||||
testname := fmt.Sprintf("duration-%s", testdata.dur)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
seconds := duration2int(testdata.dur)
|
||||
if seconds != testdata.expect {
|
||||
t.Errorf("got %d, want %d", seconds, testdata.expect)
|
||||
}
|
||||
assert.EqualValues(t, testdata.expect, seconds)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -74,9 +73,7 @@ func TestCompare(t *testing.T) {
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
c := cfg.Config{SortMode: testdata.mode, SortDescending: testdata.desc}
|
||||
got := compare(&c, testdata.a, testdata.b)
|
||||
if got != testdata.want {
|
||||
t.Errorf("got %d, want %d", got, testdata.want)
|
||||
}
|
||||
assert.EqualValues(t, testdata.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/evertras/bubble-table/table"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/tlinden/tablizer/cfg"
|
||||
)
|
||||
|
||||
@@ -118,9 +119,8 @@ var (
|
||||
Align(lipgloss.Left)
|
||||
|
||||
StyleHeader = lipgloss.NewStyle().
|
||||
Background(lipgloss.Color("#ffffff")).
|
||||
Foreground(lipgloss.Color("#696969")).
|
||||
Align(lipgloss.Left)
|
||||
Foreground(lipgloss.Color("#ff4500")).
|
||||
Align(lipgloss.Left).Bold(true)
|
||||
|
||||
// help buffer styles
|
||||
StyleKey = lipgloss.NewStyle().Bold(true)
|
||||
@@ -230,9 +230,9 @@ func NewModel(data *Tabdata, ctx *Context) FilterTable {
|
||||
|
||||
// setup column data with flexColumns
|
||||
for idx, header := range data.headers {
|
||||
// FIXME: doesn't work
|
||||
//columns[idx] = table.NewFlexColumn(strings.ToLower(header), StyleHeader.Render(header),
|
||||
columns[idx] = table.NewFlexColumn(strings.ToLower(header), header,
|
||||
columns[idx] = table.NewFlexColumn(
|
||||
strings.ToLower(header),
|
||||
StyleHeader.Render(header),
|
||||
lengths[idx]).WithFiltered(true).WithStyle(NoStyle)
|
||||
}
|
||||
|
||||
@@ -327,6 +327,7 @@ func (m *FilterTable) fillRows() {
|
||||
WithSelectedText(" ", "✓").
|
||||
WithFooterVisibility(true).
|
||||
WithHeaderVisibility(true).
|
||||
HighlightStyle(StyleSelected).
|
||||
Border(customBorder)
|
||||
}
|
||||
|
||||
@@ -468,7 +469,14 @@ func tableEditor(conf *cfg.Config, data *Tabdata) (*Tabdata, error) {
|
||||
// see https://github.com/charmbracelet/bubbletea/issues/860
|
||||
//
|
||||
// TODO: doesn't work with libgloss v2 anymore!
|
||||
lipgloss.SetDefaultRenderer(lipgloss.NewRenderer(os.Stderr))
|
||||
|
||||
out := os.Stderr
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
out = os.Stdout
|
||||
}
|
||||
|
||||
lipgloss.SetDefaultRenderer(lipgloss.NewRenderer(out))
|
||||
|
||||
ctx := &Context{data: data}
|
||||
|
||||
@@ -479,7 +487,7 @@ func tableEditor(conf *cfg.Config, data *Tabdata) (*Tabdata, error) {
|
||||
// still be used inside pipes.
|
||||
program := tea.NewProgram(
|
||||
NewModel(data, ctx),
|
||||
tea.WithOutput(os.Stderr),
|
||||
tea.WithOutput(out),
|
||||
tea.WithAltScreen())
|
||||
|
||||
m, err := program.Run()
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tiagomelo/go-clipboard/clipboard"
|
||||
"github.com/tlinden/tablizer/cfg"
|
||||
)
|
||||
@@ -59,14 +60,9 @@ func DISABLED_TestYankColumns(t *testing.T) {
|
||||
printData(&writer, conf, &data)
|
||||
|
||||
got, err := cb.PasteText()
|
||||
if err != nil {
|
||||
t.Errorf("failed to fetch yanked text from clipboard")
|
||||
}
|
||||
|
||||
if got != testdata.expect {
|
||||
t.Errorf("not yanked correctly:\n+++ got:\n%s\n+++ want:\n%s",
|
||||
got, testdata.expect)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, testdata.expect, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
# usage
|
||||
exec tablizer -h
|
||||
exec tablizer --help
|
||||
stdout Usage
|
||||
|
||||
exec tablizer -h
|
||||
stdout show
|
||||
|
||||
# version
|
||||
exec tablizer -V
|
||||
stdout version
|
||||
|
||||
148
tablizer.1
148
tablizer.1
@@ -133,7 +133,7 @@
|
||||
.\" ========================================================================
|
||||
.\"
|
||||
.IX Title "TABLIZER 1"
|
||||
.TH TABLIZER 1 "2025-08-28" "1" "User Commands"
|
||||
.TH TABLIZER 1 "2025-10-09" "1" "User Commands"
|
||||
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
|
||||
.\" way too many mistakes in technical documents.
|
||||
.if n .ad l
|
||||
@@ -144,47 +144,50 @@ tablizer \- Manipulate tabular output of other programs
|
||||
.IX Header "SYNOPSIS"
|
||||
.Vb 2
|
||||
\& Usage:
|
||||
\& tablizer [regex,...] [file, ...] [flags]
|
||||
\& tablizer [regex,...] [\-r file] [flags]
|
||||
\&
|
||||
\& Operational Flags:
|
||||
\& \-c, \-\-columns string Only show the speficied columns (separated by ,)
|
||||
\& \-v, \-\-invert\-match select non\-matching rows
|
||||
\& \-n, \-\-numbering Enable header numbering
|
||||
\& \-N, \-\-no\-color Disable pattern highlighting
|
||||
\& \-H, \-\-no\-headers Disable headers display
|
||||
\& \-s, \-\-separator string Custom field separator
|
||||
\& \-k, \-\-sort\-by int|name Sort by column (default: 1)
|
||||
\& \-z, \-\-fuzzy Use fuzzy search [experimental]
|
||||
\& \-F, \-\-filter field[!]=reg Filter given field with regex, can be used multiple times
|
||||
\& \-T, \-\-transpose\-columns string Transpose the speficied columns (separated by ,)
|
||||
\& \-R, \-\-regex\-transposer /from/to/ Apply /search/replace/ regexp to fields given in \-T
|
||||
\& \-I, \-\-interactive Interactively filter and select rows
|
||||
\& \-c, \-\-columns string Only show the speficied columns (separated by ,)
|
||||
\& \-v, \-\-invert\-match select non\-matching rows
|
||||
\& \-n, \-\-numbering Enable header numbering
|
||||
\& \-N, \-\-no\-color Disable pattern highlighting
|
||||
\& \-H, \-\-no\-headers Disable headers display
|
||||
\& \-s, \-\-separator <string> Custom field separator (maybe char, string or :class:)
|
||||
\& \-k, \-\-sort\-by <int|name> Sort by column (default: 1)
|
||||
\& \-z, \-\-fuzzy Use fuzzy search [experimental]
|
||||
\& \-F, \-\-filter <field[!]=reg> Filter given field with regex, can be used multiple times
|
||||
\& \-T, \-\-transpose\-columns string Transpose the speficied columns (separated by ,)
|
||||
\& \-R, \-\-regex\-transposer </from/to/> Apply /search/replace/ regexp to fields given in \-T
|
||||
\& \-j, \-\-json Read JSON input (must be array of hashes)
|
||||
\& \-I, \-\-interactive Interactively filter and select rows
|
||||
\&
|
||||
\& 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 output
|
||||
\& \-Y, \-\-yaml Enable yaml output
|
||||
\& \-C, \-\-csv Enable CSV output
|
||||
\& \-A, \-\-ascii Default output mode, ascii tabular
|
||||
\& \-L, \-\-hightlight\-lines Use alternating background colors for tables
|
||||
\& \-y, \-\-yank\-columns Yank specified columns (separated by ,) to clipboard,
|
||||
\& space separated
|
||||
\& \-X, \-\-extended Enable extended output
|
||||
\& \-M, \-\-markdown Enable markdown table output
|
||||
\& \-O, \-\-orgtbl Enable org\-mode table output
|
||||
\& \-S, \-\-shell Enable shell evaluable output
|
||||
\& \-Y, \-\-yaml Enable yaml output
|
||||
\& \-C, \-\-csv Enable CSV output
|
||||
\& \-A, \-\-ascii Default output mode, ascii tabular
|
||||
\& \-L, \-\-hightlight\-lines Use alternating background colors for tables
|
||||
\& \-y, \-\-yank\-columns Yank specified columns (separated by ,) to clipboard,
|
||||
\& space separated
|
||||
\& \-\-ofs <char> Output field separator, used by \-A and \-C.
|
||||
\&
|
||||
\& 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
|
||||
\& \-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>
|
||||
\& \-f, \-\-config <file> Configuration file (default: ~/.config/tablizer/config)
|
||||
\& \-d, \-\-debug Enable debugging
|
||||
\& \-h, \-\-help help for tablizer
|
||||
\& \-m, \-\-man Display manual page
|
||||
\& \-V, \-\-version Print program version
|
||||
\& \-r \-\-read\-file <file> Use <file> as input instead of STDIN
|
||||
\& \-\-completion <shell> Generate the autocompletion script for <shell>
|
||||
\& \-f, \-\-config <file> Configuration file (default: ~/.config/tablizer/config)
|
||||
\& \-d, \-\-debug Enable debugging
|
||||
\& \-h, \-\-help help for tablizer
|
||||
\& \-m, \-\-man Display manual page
|
||||
\& \-V, \-\-version Print program version
|
||||
.Ve
|
||||
.SH "DESCRIPTION"
|
||||
.IX Header "DESCRIPTION"
|
||||
@@ -216,17 +219,17 @@ pattern. Hence:
|
||||
\& kubectl get pods | tablizer
|
||||
\&
|
||||
\& # read a file
|
||||
\& tablizer filename
|
||||
\& tablizer \-r filename
|
||||
\&
|
||||
\& # search for pattern in a file (works like grep)
|
||||
\& tablizer regex filename
|
||||
\& tablizer regex \-r filename
|
||||
\&
|
||||
\& # search for pattern in STDIN
|
||||
\& kubectl get pods | tablizer regex
|
||||
.Ve
|
||||
.PP
|
||||
The output looks like the original one but every header field will
|
||||
have a numer associated with it, e.g.:
|
||||
The output looks like the original one. You can add the option \fB\-n\fR,
|
||||
then every header field will have a numer associated with it, e.g.:
|
||||
.PP
|
||||
.Vb 1
|
||||
\& NAME(1) READY(2) STATUS(3) RESTARTS(4) AGE(5)
|
||||
@@ -242,7 +245,18 @@ columns you want to have in your output (see \s-1COLUMNS\s0:
|
||||
You can specify the numbers in any order but output will always follow
|
||||
the original order.
|
||||
.PP
|
||||
The numbering can be suppressed by using the \fB\-n\fR option.
|
||||
However, you may also just use the header names instead of numbers,
|
||||
eg:
|
||||
.PP
|
||||
.Vb 1
|
||||
\& 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
|
||||
.Ve
|
||||
.PP
|
||||
By default tablizer shows a header containing the names of each
|
||||
column. This can be disabled using the \fB\-H\fR option. Be aware that
|
||||
@@ -279,6 +293,62 @@ Sorts timestamps.
|
||||
.PP
|
||||
Finally the \fB\-d\fR option enables debugging output which is mostly
|
||||
useful for the developer.
|
||||
.SS "\s-1SEPARATOR\s0"
|
||||
.IX Subsection "SEPARATOR"
|
||||
The option \fB\-s\fR can be a single character, in which case the \s-1CSV\s0
|
||||
parser will be invoked. You can also specify a string as
|
||||
separator. The string will be interpreted as literal string unless it
|
||||
is a valid go regular expression. For example:
|
||||
.PP
|
||||
.Vb 1
|
||||
\& \-s \*(Aq\et{2,}\e\*(Aq
|
||||
.Ve
|
||||
.PP
|
||||
is being used as a regexp and will match two or more consecutive tabs.
|
||||
.PP
|
||||
.Vb 1
|
||||
\& \-s \*(Aqfoo\*(Aq
|
||||
.Ve
|
||||
.PP
|
||||
on the other hand is no regular expression and will be used literally.
|
||||
.PP
|
||||
To make live easier, there are a couple of predefined regular
|
||||
expressions, which you can specify as classes:
|
||||
.Sp
|
||||
.RS 4
|
||||
* :tab:
|
||||
.Sp
|
||||
Matches a tab and eats spaces around it.
|
||||
.Sp
|
||||
* :spaces:
|
||||
.Sp
|
||||
Matches 2 or more spaces.
|
||||
.Sp
|
||||
* :pipe:
|
||||
.Sp
|
||||
Matches a pipe character and eats spaces around it.
|
||||
.Sp
|
||||
* :default:
|
||||
.Sp
|
||||
Matches 2 or more spaces or tab. This is the default separator if none
|
||||
is specified.
|
||||
.Sp
|
||||
* :nonword:
|
||||
.Sp
|
||||
Matches a non-word character.
|
||||
.Sp
|
||||
* :nondigit:
|
||||
.Sp
|
||||
Matches a non-digit character.
|
||||
.Sp
|
||||
* :special:
|
||||
.Sp
|
||||
Matches one or more special chars like brackets, dollar sign, slashes etc.
|
||||
.Sp
|
||||
* :nonprint:
|
||||
.Sp
|
||||
Matches one or more non-printable characters.
|
||||
.RE
|
||||
.SS "\s-1PATTERNS AND FILTERING\s0"
|
||||
.IX Subsection "PATTERNS AND FILTERING"
|
||||
You can reduce the rows being displayed by using one or more regular
|
||||
|
||||
142
tablizer.pod
142
tablizer.pod
@@ -5,47 +5,50 @@ tablizer - Manipulate tabular output of other programs
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage:
|
||||
tablizer [regex,...] [file, ...] [flags]
|
||||
tablizer [regex,...] [-r file] [flags]
|
||||
|
||||
Operational Flags:
|
||||
-c, --columns string Only show the speficied columns (separated by ,)
|
||||
-v, --invert-match select non-matching rows
|
||||
-n, --numbering Enable header numbering
|
||||
-N, --no-color Disable pattern highlighting
|
||||
-H, --no-headers Disable headers display
|
||||
-s, --separator string Custom field separator
|
||||
-k, --sort-by int|name Sort by column (default: 1)
|
||||
-z, --fuzzy Use fuzzy search [experimental]
|
||||
-F, --filter field[!]=reg Filter given field with regex, can be used multiple times
|
||||
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
|
||||
-R, --regex-transposer /from/to/ Apply /search/replace/ regexp to fields given in -T
|
||||
-I, --interactive Interactively filter and select rows
|
||||
-c, --columns string Only show the speficied columns (separated by ,)
|
||||
-v, --invert-match select non-matching rows
|
||||
-n, --numbering Enable header numbering
|
||||
-N, --no-color Disable pattern highlighting
|
||||
-H, --no-headers Disable headers display
|
||||
-s, --separator <string> Custom field separator (maybe char, string or :class:)
|
||||
-k, --sort-by <int|name> Sort by column (default: 1)
|
||||
-z, --fuzzy Use fuzzy search [experimental]
|
||||
-F, --filter <field[!]=reg> Filter given field with regex, can be used multiple times
|
||||
-T, --transpose-columns string Transpose the speficied columns (separated by ,)
|
||||
-R, --regex-transposer </from/to/> Apply /search/replace/ regexp to fields given in -T
|
||||
-j, --json Read JSON input (must be array of hashes)
|
||||
-I, --interactive Interactively filter and select rows
|
||||
|
||||
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 output
|
||||
-Y, --yaml Enable yaml output
|
||||
-C, --csv Enable CSV output
|
||||
-A, --ascii Default output mode, ascii tabular
|
||||
-L, --hightlight-lines Use alternating background colors for tables
|
||||
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
|
||||
space separated
|
||||
-X, --extended Enable extended output
|
||||
-M, --markdown Enable markdown table output
|
||||
-O, --orgtbl Enable org-mode table output
|
||||
-S, --shell Enable shell evaluable output
|
||||
-Y, --yaml Enable yaml output
|
||||
-C, --csv Enable CSV output
|
||||
-A, --ascii Default output mode, ascii tabular
|
||||
-L, --hightlight-lines Use alternating background colors for tables
|
||||
-y, --yank-columns Yank specified columns (separated by ,) to clipboard,
|
||||
space separated
|
||||
--ofs <char> Output field separator, used by -A and -C.
|
||||
|
||||
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
|
||||
-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>
|
||||
-f, --config <file> Configuration file (default: ~/.config/tablizer/config)
|
||||
-d, --debug Enable debugging
|
||||
-h, --help help for tablizer
|
||||
-m, --man Display manual page
|
||||
-V, --version Print program version
|
||||
-r --read-file <file> Use <file> as input instead of STDIN
|
||||
--completion <shell> Generate the autocompletion script for <shell>
|
||||
-f, --config <file> Configuration file (default: ~/.config/tablizer/config)
|
||||
-d, --debug Enable debugging
|
||||
-h, --help help for tablizer
|
||||
-m, --man Display manual page
|
||||
-V, --version Print program version
|
||||
|
||||
|
||||
=head1 DESCRIPTION
|
||||
@@ -77,16 +80,16 @@ pattern. Hence:
|
||||
kubectl get pods | tablizer
|
||||
|
||||
# read a file
|
||||
tablizer filename
|
||||
tablizer -r filename
|
||||
|
||||
# search for pattern in a file (works like grep)
|
||||
tablizer regex filename
|
||||
tablizer regex -r filename
|
||||
|
||||
# search for pattern in STDIN
|
||||
kubectl get pods | tablizer regex
|
||||
|
||||
The output looks like the original one but every header field will
|
||||
have a numer associated with it, e.g.:
|
||||
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.:
|
||||
|
||||
NAME(1) READY(2) STATUS(3) RESTARTS(4) AGE(5)
|
||||
|
||||
@@ -98,7 +101,14 @@ columns you want to have in your output (see L<COLUMNS>:
|
||||
You can specify the numbers in any order but output will always follow
|
||||
the original order.
|
||||
|
||||
The numbering can be suppressed by using the B<-n> option.
|
||||
However, you may also just use the header names instead of numbers,
|
||||
eg:
|
||||
|
||||
kubectl get pods | tablizer -cname,status
|
||||
|
||||
You can also use regular expressions with B<-c>, eg:
|
||||
|
||||
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
|
||||
@@ -143,6 +153,62 @@ Sorts timestamps.
|
||||
Finally the B<-d> option enables debugging output which is mostly
|
||||
useful for the developer.
|
||||
|
||||
=head2 SEPARATOR
|
||||
|
||||
The option B<-s> can be a single character, in which case the CSV
|
||||
parser will be invoked. You can also specify a string as
|
||||
separator. The string will be interpreted as literal string unless it
|
||||
is a valid go regular expression. For example:
|
||||
|
||||
-s '\t{2,}\'
|
||||
|
||||
is being used as a regexp and will match two or more consecutive tabs.
|
||||
|
||||
-s 'foo'
|
||||
|
||||
on the other hand is no regular expression and will be used literally.
|
||||
|
||||
To make live easier, there are a couple of predefined regular
|
||||
expressions, which you can specify as classes:
|
||||
|
||||
=over
|
||||
|
||||
* :tab:
|
||||
|
||||
Matches a tab and eats spaces around it.
|
||||
|
||||
* :spaces:
|
||||
|
||||
Matches 2 or more spaces.
|
||||
|
||||
* :pipe:
|
||||
|
||||
Matches a pipe character and eats spaces around it.
|
||||
|
||||
* :default:
|
||||
|
||||
Matches 2 or more spaces or tab. This is the default separator if none
|
||||
is specified.
|
||||
|
||||
* :nonword:
|
||||
|
||||
Matches a non-word character.
|
||||
|
||||
* :nondigit:
|
||||
|
||||
Matches a non-digit character.
|
||||
|
||||
* :special:
|
||||
|
||||
Matches one or more special chars like brackets, dollar sign, slashes etc.
|
||||
|
||||
* :nonprint:
|
||||
|
||||
Matches one or more non-printable characters.
|
||||
|
||||
|
||||
=back
|
||||
|
||||
=head2 PATTERNS AND FILTERING
|
||||
|
||||
You can reduce the rows being displayed by using one or more regular
|
||||
|
||||
15
vhsdemo/Makefile
Normal file
15
vhsdemo/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
.PHONY: demo check clean-demo
|
||||
|
||||
VHS = vhs
|
||||
|
||||
clean-demo:
|
||||
|
||||
%.gif: %.tape
|
||||
@echo "vhs $<"
|
||||
env PATH=..:$(PATH) vhs $<
|
||||
|
||||
check:
|
||||
ls -l ../tablizer
|
||||
|
||||
demo: check clean-demo demo.gif
|
||||
|
||||
BIN
vhsdemo/demo.gif
Normal file
BIN
vhsdemo/demo.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.1 MiB |
157
vhsdemo/demo.tape
Normal file
157
vhsdemo/demo.tape
Normal file
@@ -0,0 +1,157 @@
|
||||
# -*-sh-*-
|
||||
|
||||
Output demo.gif
|
||||
Set FontSize 20
|
||||
Set Width 1200
|
||||
Set Height 1000
|
||||
Set Theme { "name": "Whimsy", "black": "#535178", "red": "#ef6487", "green": "#5eca89", "yellow": "#fdd877", "blue": "#65aef7", "magenta": "#aa7ff0", "cyan": "#43c1be", "white": "#ffffff", "brightBlack": "#535178", "brightRed": "#ef6487", "brightGreen": "#5eca89", "brightYellow": "#fdd877", "brightBlue": "#65aef7", "brightMagenta": "#aa7ff0", "brightCyan": "#43c1be", "brightWhite": "#ffffff", "background": "#29283b", "foreground": "#b3b0d6", "selection": "#3d3c58", "cursor": "#b3b0d6" }
|
||||
Set WindowBar Colorful
|
||||
Set BorderRadius 10
|
||||
Set Shell zsh
|
||||
Set FontFamily "IBM Plex Mono"
|
||||
Set CursorBlink false
|
||||
Set PlaybackSpeed 1
|
||||
Set TypingSpeed .05
|
||||
|
||||
# initialize
|
||||
Hide
|
||||
Type `PROMPT=''`
|
||||
Enter
|
||||
Type "setopt interactivecomments"
|
||||
Enter
|
||||
Type "autoload -U colors && colors"
|
||||
Enter
|
||||
Type `PS1="%{$fg[magenta]%}demo> %{$reset_color%}"`
|
||||
Enter
|
||||
Type "clear"
|
||||
Enter
|
||||
Show
|
||||
|
||||
Type "# Our input data"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "cat input | head -10"
|
||||
Enter
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# Filter over all rows"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "tablizer Central < input"
|
||||
Enter
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# Filter over all rows case insensitive"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "tablizer '/penc/i' < input"
|
||||
Enter
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# Filter over specific column"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "tablizer -Fcost=4.99 < input"
|
||||
Enter
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# Filter by regex on specific column"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "tablizer -Funits=Pen. < input"
|
||||
Enter
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# Output as markdown"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "tablizer -Funits=Pen. -M < input"
|
||||
Enter
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# Output as CSV"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "tablizer -Funits=Pen. -C < input"
|
||||
Enter
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# Output as shell evaluable"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "tablizer -Funits=Pen. -S < input"
|
||||
Enter
|
||||
Sleep 2s
|
||||
Type "bat eval.sh"
|
||||
Enter
|
||||
Sleep 2s
|
||||
Type "tablizer -Funits=Pen. -S < input | ./eval.sh"
|
||||
Enter
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# Reduce columns"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "tablizer -Funits=Pen. -c region,customer,units,count < input"
|
||||
Enter
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# Sort by COUNT column numerically "
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "tablizer -Funits=Pen. -c region,customer,units,count -kcount -i < input"
|
||||
Enter
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# Do further filtering interactively"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "tablizer -Funits=Pen. -c region,customer,units,count -I -O < input"
|
||||
Enter
|
||||
Sleep 2s
|
||||
Type "?"
|
||||
Sleep 2s
|
||||
Type "/"
|
||||
Sleep 2s
|
||||
Type "J"
|
||||
Sleep 1s
|
||||
Type "o"
|
||||
Sleep 1s
|
||||
Type "n"
|
||||
Sleep 1s
|
||||
Type "e"
|
||||
Sleep 1s
|
||||
Type "s"
|
||||
Sleep 1s
|
||||
Enter
|
||||
Sleep 2s
|
||||
Tab
|
||||
Sleep 1s
|
||||
Tab
|
||||
Sleep 1s
|
||||
Tab
|
||||
Sleep 1s
|
||||
Tab
|
||||
Type "n"
|
||||
Sleep 2s
|
||||
Space
|
||||
Sleep 1s
|
||||
Down
|
||||
Sleep 1s
|
||||
Down
|
||||
Sleep 1s
|
||||
Space
|
||||
Sleep 2s
|
||||
Type "q"
|
||||
|
||||
Sleep 10s
|
||||
5
vhsdemo/eval.sh
Executable file
5
vhsdemo/eval.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
while read LINE; do
|
||||
eval "$LINE"; echo "$Customer ordered $Count ${Units}s"
|
||||
done
|
||||
44
vhsdemo/input
Normal file
44
vhsdemo/input
Normal file
@@ -0,0 +1,44 @@
|
||||
Date Region Customer Units Count Cost Total
|
||||
2016-01-06 East Jones Pencil 95 1.99 189.05
|
||||
2016-01-23 Central Kivell Binder 50 19.99 999.50
|
||||
2016-02-09 Central Jardine Pencil 36 4.99 179.64
|
||||
2016-02-26 Central Gill Pen 27 19.99 539.73
|
||||
2016-03-15 West Sorvino Pencil 56 2.99 167.44
|
||||
2016-04-01 East Jones Binder 60 4.99 299.40
|
||||
2016-04-18 Central Andrews Pencil 75 1.99 149.25
|
||||
2016-05-05 Central Jardine Pencil 90 4.99 449.10
|
||||
2016-05-22 West Thompson Pencil 32 1.99 63.68
|
||||
2016-06-08 East Jones Binder 60 8.99 539.40
|
||||
2016-06-25 Central Morgan Pencil 90 4.99 449.10
|
||||
2016-07-12 East Howard Binder 29 1.99 57.71
|
||||
2016-07-29 East Parent Binder 81 19.99 1619.19
|
||||
2016-08-15 East Jones Pencil 35 4.99 174.65
|
||||
2016-09-01 Central Smith Desk 2 125.00 250.00
|
||||
2016-09-18 East Jones Pen Set 16 15.99 255.84
|
||||
2016-10-05 Central Morgan Binder 28 8.99 251.72
|
||||
2016-10-22 East Jones Pen 64 8.99 575.36
|
||||
2016-11-08 East Parent Pen 15 19.99 299.85
|
||||
2016-11-25 Central Kivell Pen Set 96 4.99 479.04
|
||||
2016-12-12 Central Smith Pencil 67 1.29 86.43
|
||||
2016-12-29 East Parent Pen Set 74 15.99 1183.26
|
||||
2017-01-15 Central Gill Binder 46 8.99 413.54
|
||||
2017-02-01 Central Smith Binder 87 15.00 1305.00
|
||||
2017-02-18 East Jones Binder 4 4.99 19.96
|
||||
2017-03-07 West Sorvino Binder 7 19.99 139.93
|
||||
2017-03-24 Central Jardine Pen Set 50 4.99 249.50
|
||||
2017-04-10 Central Andrews Pencil 66 1.99 131.34
|
||||
2017-04-27 East Howard Pen 96 4.99 479.04
|
||||
2017-05-14 Central Gill Pencil 53 1.29 68.37
|
||||
2017-05-31 Central Gill Binder 80 8.99 719.20
|
||||
2017-06-17 Central Kivell Desk 5 125.00 625.00
|
||||
2017-07-04 East Jones Pen Set 62 4.99 309.38
|
||||
2017-07-21 Central Morgan Pen Set 55 12.49 686.95
|
||||
2017-08-07 Central Kivell Pen Set 42 23.95 1005.90
|
||||
2017-08-24 West Sorvino Desk 3 275.00 825.00
|
||||
2017-09-10 Central Gill Pencil 7 1.29 9.03
|
||||
2017-09-27 West Sorvino Pen 76 1.99 151.24
|
||||
2017-10-14 West Thompson Binder 57 19.99 1139.43
|
||||
2017-10-31 Central Andrews Pencil 14 1.29 18.06
|
||||
2017-11-17 Central Jardine Binder 11 4.99 54.89
|
||||
2017-12-04 Central Jardine Binder 94 19.99 1879.06
|
||||
2017-12-21 Central Andrews Binder 28 4.99 139.72
|
||||
Reference in New Issue
Block a user