Compare commits

..

190 Commits

Author SHA1 Message Date
041feccb0b moved to codeberg 2025-11-03 22:07:53 +01:00
T. von Dein
bd5ee90324 move to codeberg (#45) 2025-11-03 22:06:11 +01:00
bc717baa3f fix typo 2025-10-25 21:51:56 +02:00
c34f030914 add stew 2025-10-25 21:50:31 +02:00
T.v.Dein
f1aa9d0000 add json output mode (-J) (#87) 2025-10-14 07:18:30 +02:00
736dd37f16 fixed feature entry 2025-10-13 07:24:35 +02:00
e0dc6bb845 updated and added feature list 2025-10-13 07:23:54 +02:00
T.v.Dein
8bdb3db105 fix #85: add --auto-headers and --custom-headers (#86) 2025-10-10 13:08:16 +02:00
4ce6c30f54 fix short usage formatting 2025-10-09 23:16:07 +02:00
T.v.Dein
ec0b210167 add some handy builtin character classes as split separators (#84) 2025-10-09 23:03:57 +02:00
253ef8262e fix builder go version 2025-10-08 10:36:09 +02:00
da48994744 fix comment 2025-10-06 23:27:48 +02:00
39f06fddc8 md fix 2025-10-06 23:02:28 +02:00
T.v.Dein
50a9378d92 use column order of -c when specified (#81) 2025-10-06 22:55:04 +02:00
T.v.Dein
35b726fee4 Fix json parser (#80)
* fix #77: parse floats and nils as well and convert them to string
2025-10-06 22:54:31 +02:00
T.v.Dein
8c87da34f2 show short help with -h (#76) 2025-10-02 21:34:38 +02:00
dependabot[bot]
6f0f5afb27 Bump actions/setup-go from 5 to 6 (#68) 2025-10-01 21:16:48 +02:00
T.v.Dein
62b606e7da use 1.24 for CI (#75) 2025-10-01 21:14:18 +02:00
dependabot[bot]
567d23b175 Bump github.com/alecthomas/repr from 0.5.1 to 0.5.2 (#69) 2025-10-01 21:08:36 +02:00
dependabot[bot]
14f24533f0 Bump github.com/spf13/cobra from 1.9.1 to 1.10.1 (#70) 2025-10-01 21:04:25 +02:00
dependabot[bot]
4e413c02b5 Bump github.com/charmbracelet/bubbletea from 1.3.6 to 1.3.10 (#71) 2025-10-01 21:01:04 +02:00
dependabot[bot]
6d8c0c0936 Bump github.com/evertras/bubble-table from 0.19.0 to 0.19.2 (#72) 2025-10-01 20:57:58 +02:00
dependabot[bot]
21b607af7c Bump github.com/olekukonko/tablewriter from 1.0.9 to 1.1.0 (#73) 2025-10-01 20:54:31 +02:00
T.v.Dein
06a5d74fb6 Add JSON input support (#74)
* added basic json input support
* add coverage to make test
* enhanced unit tests, switch to testify/assert
* reduce ci runs
2025-10-01 20:48:49 +02:00
T.v.Dein
5f3f7c417c Improve ascii table, add --ofs flag, enhance documentation (#67)
* enhanced documentation
* added --ofs parameter
  use 2 spaces for ascii output but it is customizable with --ofs,
  which is also being used by CSV mode (whose defaults remains unchanged)
* improve ascii table output, use 2 spaces as OFS by default
2025-09-30 11:21:49 +02:00
T.v.Dein
687f4b7bb2 Fix excess spaces on normal ascii table output (#66) 2025-09-29 20:46:33 +02:00
24b66b8a6b mv demo to top 2025-09-11 19:12:49 +02:00
d87c6878a4 bump version 2025-09-11 19:04:38 +02:00
4cdc4c8e18 switched to vhs demo creator 2025-09-11 19:04:02 +02:00
9cb9a66332 fix #64: documented -r parameter 2025-09-11 19:01:11 +02:00
24277cd716 use stdout if it is a tty 2025-09-11 19:00:37 +02:00
e51b141032 bump version 2025-09-10 12:17:09 +02:00
7af7304529 header cosmetics 2025-09-10 12:16:53 +02:00
b4c833a0ba also style selected row 2025-09-10 12:13:34 +02:00
1c36d93d65 add gh-dash config 2025-09-09 22:06:07 +02:00
T.v.Dein
ec864f42d6 added styled help buffer (#63) 2025-09-09 22:01:45 +02:00
dependabot[bot]
4eaa676510 Bump github.com/gookit/color from 1.5.4 to 1.6.0 (#60) 2025-09-09 20:36:44 +02:00
dependabot[bot]
c600fb1136 Bump actions/checkout from 4 to 5 (#59) 2025-09-09 20:10:03 +02:00
dependabot[bot]
abf9fac5c7 Bump github.com/charmbracelet/bubbletea from 1.3.4 to 1.3.6 (#61) 2025-09-09 20:09:35 +02:00
T.v.Dein
80dd6849ae Add interactive filter table (#62) 2025-09-09 20:09:08 +02:00
e2b82515f5 bump version 2025-08-28 21:09:32 +02:00
T.v.Dein
1976b4046e Add interactive filter/selection tool (#58) 2025-08-28 21:08:28 +02:00
dependabot[bot]
b1a2b3059e Bump github.com/alecthomas/repr from 0.4.0 to 0.5.1 (#55) 2025-08-26 10:44:10 +02:00
dependabot[bot]
e3d6ef130c Bump github.com/hashicorp/hcl/v2 from 2.23.0 to 2.24.0 (#56) 2025-08-26 10:34:24 +02:00
dependabot[bot]
92fffaae9a Bump github.com/olekukonko/tablewriter from 1.0.6 to 1.0.9 (#54) 2025-08-26 10:28:54 +02:00
T.v.Dein
f1c5ee5797 replace pipe to less with bubbletea internal pager (#57) 2025-08-26 10:22:03 +02:00
T.v.Dein
5168b04339 optimize ascii tables, use custom style instead of tab inserters (#53)
* optimize ascii tables, use custom style instead of tab inserters
2025-06-10 16:12:03 +02:00
T.v.Dein
787178b17e Update to tableWriter 1.0.6 (#50) 2025-05-27 13:09:18 +02:00
T.v.Dein
eae39bbff1 Merge pull request #48 from TLINDEN/updatego
update to go1.23, update dependencies
2025-03-06 17:28:42 +01:00
40fbf17779 also update ci to go 1.23 2025-03-06 17:25:54 +01:00
832841c1ff deprecate testscript.RunMain() 2025-03-06 17:24:16 +01:00
5726ed3f7f update to go1.23, update dependencies 2025-03-06 17:16:20 +01:00
T.v.Dein
5e52cd9ce0 Merge pull request #45 from TLINDEN/dependabot/go_modules/github.com/spf13/cobra-1.9.1
Bump github.com/spf13/cobra from 1.8.1 to 1.9.1
2025-03-06 17:11:24 +01:00
T.v.Dein
8c7c89c9ea Merge pull request #47 from TLINDEN/headernumbers
reverse the meaning of -n
2025-03-06 17:11:00 +01:00
25aa172c41 reverse the meaning of -n, setting it enables numbered headers 2025-03-06 17:02:34 +01:00
dependabot[bot]
c436a92bcb Bump github.com/spf13/cobra from 1.8.1 to 1.9.1
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.8.1 to 1.9.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.8.1...v1.9.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-01 10:40:32 +00:00
T.v.Dein
65732a58d0 Merge pull request #38 from TLINDEN/feature/yank
add yank support
2025-02-23 18:21:15 +01:00
T.v.Dein
ace7f76210 Merge branch 'main' into feature/yank 2025-02-23 18:09:04 +01:00
fda365bd8b bump version 2025-02-23 18:06:42 +01:00
c1cfc08c23 fix windows test, add clean to test target 2025-02-23 18:02:52 +01:00
150fdddd2a use latest go-clipboard 2025-02-23 18:00:29 +01:00
6b659773f1 build release bins w/o symbols and debug, +static 2025-02-19 18:09:05 +01:00
74d82fa356 fix ci tests on windows: make clean before running test 2025-02-12 14:08:04 +01:00
3949411c57 add change log generator, update release builder 2025-02-05 17:51:14 +01:00
a455f6b79a bump version 2025-01-30 17:31:56 +01:00
2c08687c29 add support for negative filters (-F field!=regex) 2025-01-30 17:31:26 +01:00
200f1f32f8 using patched tiagomeol/go-clipboard/clipboard, fixes #37 2025-01-28 14:40:17 +01:00
768a19b4d6 fine tuning, added test, which hangs, but yanking works anyway 2025-01-23 13:59:02 +01:00
Thomas von Dein
dc718392b6 fix-import 2025-01-22 23:15:12 +01:00
Thomas von Dein
e8f4fef41c fix #37: make yank portable 2025-01-22 23:12:42 +01:00
6566dd66f0 fixed pattern regex, fixed pattern AND operation 2025-01-22 17:53:10 +01:00
1593799c03 added multi pattern tests 2025-01-22 17:53:10 +01:00
ea3dd75fec fix linting error 2025-01-22 17:53:10 +01:00
a306f2c601 implement multiple regex support and icase and negate flags 2025-01-22 17:53:10 +01:00
82f54c120d catch err 2025-01-21 18:42:04 +01:00
T.v.Dein
2d5799e2f2 Use primary clipboard on unix 2025-01-20 21:27:26 +01:00
8e33cadcaa add -y 2025-01-20 19:28:19 +01:00
03f3225f24 build release binaries using ci workflow 2025-01-18 10:51:28 +01:00
63c7ef26b6 add -k<name> and sort by multiple columns support, fixes #34 2025-01-15 18:53:34 +01:00
dependabot[bot]
c2e7d8037a Bump github.com/hashicorp/hcl/v2 from 2.22.0 to 2.23.0
Bumps [github.com/hashicorp/hcl/v2](https://github.com/hashicorp/hcl) from 2.22.0 to 2.23.0.
- [Release notes](https://github.com/hashicorp/hcl/releases)
- [Changelog](https://github.com/hashicorp/hcl/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/hcl/compare/v2.22.0...v2.23.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/hcl/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-14 13:12:06 +01:00
323c070caa tests don't work on windows 2025-01-14 13:10:09 +01:00
53cf1e2ebe fix for windows 2025-01-14 13:10:09 +01:00
16c5053752 satisfy linter 2025-01-14 13:10:09 +01:00
7d2d9a55d3 added prior art, fixes #30 as well 2025-01-14 13:10:09 +01:00
14c50b4e63 get rid of lisp interpreter, -R and -F are enough, fixes #30 2025-01-14 13:10:09 +01:00
0e68dc585d added testscript test to test the combination of all tasks 2025-01-14 13:10:09 +01:00
6ca835add1 changed file handling, use -r <file> or nothing to use stdin 2025-01-14 13:10:09 +01:00
306f583522 fixed transpose error message if count is incorrect 2025-01-14 13:10:09 +01:00
9f971ed3b9 fix #32: treat header filters case insensitively 2025-01-14 13:10:09 +01:00
2ae2d2b33d add transpose stuff to README, bump version 2025-01-14 13:10:09 +01:00
cf1a555b9b added tests, reorganized Parse() by dismantling parsing and processing 2025-01-14 13:10:09 +01:00
4d894a728b added transpose function (-T + -R) 2025-01-14 13:10:09 +01:00
8792c5a40f fix regex in example 2025-01-10 18:33:55 +01:00
7ab1a1178a add zygo reference 2025-01-10 18:27:41 +01:00
1e44da4f6e added documentation about current state of lisp support 2025-01-10 18:26:33 +01:00
59171f0fab bump versions 2024-12-13 10:37:44 +01:00
8098ccf000 fix #29: fix stat() error checking 2024-12-13 10:35:56 +01:00
4dc87ac22e fix #27: check if parsed headers and columns match 2024-11-04 11:13:53 +01:00
ef5211e45f only works on 1.22 2024-09-27 11:15:23 +02:00
1a80e72737 fix version quoting 2024-09-27 11:04:32 +02:00
8e765b167f ok, only test with 1.22.1 2024-09-25 18:55:04 +02:00
30f4b67538 bump version and add current go versions for testing 2024-09-25 18:52:47 +02:00
383b5db47e try 1.22.1 2024-09-25 18:40:13 +02:00
f7d812b372 try to quote go version 2024-09-25 18:38:06 +02:00
480f5f617d only try 1.20 2024-09-25 18:33:46 +02:00
586e36c181 update to go 1.22 2024-09-25 18:24:35 +02:00
dependabot[bot]
13c789b800 Bump github.com/alecthomas/repr from 0.1.1 to 0.4.0
Bumps [github.com/alecthomas/repr](https://github.com/alecthomas/repr) from 0.1.1 to 0.4.0.
- [Commits](https://github.com/alecthomas/repr/compare/v0.1.1...v0.4.0)

---
updated-dependencies:
- dependency-name: github.com/alecthomas/repr
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-25 18:22:10 +02:00
dependabot[bot]
81e1394fd2 Bump github.com/gookit/color from 1.5.2 to 1.5.4
Bumps [github.com/gookit/color](https://github.com/gookit/color) from 1.5.2 to 1.5.4.
- [Release notes](https://github.com/gookit/color/releases)
- [Commits](https://github.com/gookit/color/compare/v1.5.2...v1.5.4)

---
updated-dependencies:
- dependency-name: github.com/gookit/color
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-25 18:22:00 +02:00
dependabot[bot]
b8099fe389 Bump github.com/spf13/cobra from 1.6.1 to 1.8.1
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.6.1 to 1.8.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.6.1...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-25 18:21:46 +02:00
dependabot[bot]
1dc072aa67 Bump github.com/lithammer/fuzzysearch from 1.1.7 to 1.1.8
Bumps [github.com/lithammer/fuzzysearch](https://github.com/lithammer/fuzzysearch) from 1.1.7 to 1.1.8.
- [Release notes](https://github.com/lithammer/fuzzysearch/releases)
- [Commits](https://github.com/lithammer/fuzzysearch/compare/v1.1.7...v1.1.8)

---
updated-dependencies:
- dependency-name: github.com/lithammer/fuzzysearch
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-25 18:21:23 +02:00
dependabot[bot]
d92f63ca30 Bump github.com/hashicorp/hcl/v2 from 2.19.1 to 2.22.0
Bumps [github.com/hashicorp/hcl/v2](https://github.com/hashicorp/hcl) from 2.19.1 to 2.22.0.
- [Release notes](https://github.com/hashicorp/hcl/releases)
- [Changelog](https://github.com/hashicorp/hcl/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/hcl/compare/v2.19.1...v2.22.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/hcl/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-25 18:13:50 +02:00
78ccb8f54b use non format logger 2024-09-25 18:06:00 +02:00
a29104aeab fix typos in issue templates 2024-09-25 18:06:00 +02:00
dependabot[bot]
45d9e219a5 Bump golangci/golangci-lint-action from 3 to 6
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 3 to 6.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v3...v6)

---
updated-dependencies:
- dependency-name: golangci/golangci-lint-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-25 16:40:10 +02:00
dependabot[bot]
3eda59beeb Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-25 16:39:58 +02:00
dependabot[bot]
7ada75c1d6 Bump actions/setup-go from 3 to 5
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-25 16:39:36 +02:00
83d5628430 add dependabot config 2024-09-25 16:33:54 +02:00
a9bb79b01c merge corrections 2024-05-07 18:39:38 +02:00
a718fa388d Merge remote-tracking branch 'origin/development' 2024-05-07 18:29:01 +02:00
473feff451 refactored and un-go-criticed 2024-05-07 18:01:12 +02:00
9e2e45715e added -F docs 2024-05-07 18:00:57 +02:00
39609425e5 refactoring and gouncritic, 1st part 2024-05-07 15:19:54 +02:00
ba2a2e8460 add -F filter by column flag (closes #13) 2024-05-07 13:30:07 +02:00
96f7881c16 fix #12: only consider -v if there's a pattern, ignore it otherwise 2024-05-07 13:29:41 +02:00
T.v.Dein
6fccd1287b Feature additions (#11)
* add color table support (using alternating colorization of rows) using new flag `-L`
* add config file support (HCL format) using `~/.config/tablizer/config` or `-f <file>` so the user can customize colors
* removed golang 1.17 support
2023-11-22 14:16:43 +01:00
0f22457961 remove go 1.17 support 2023-11-22 14:09:49 +01:00
ddfbecaa35 +docs, try linter v1.18 2023-11-22 14:08:36 +01:00
3632de10d7 try to fix linter 2023-11-22 13:57:57 +01:00
76b98fb8ad upd mods 2023-11-22 13:48:32 +01:00
f045adf441 added config file support to set custom colors 2023-11-22 13:33:26 +01:00
811173ddb4 fixed alternating highlighting, now looks reasonable 2023-11-22 10:30:40 +01:00
3c910ca08f works but is ugly :( 2023-11-21 17:41:04 +01:00
a8c9ede77e added -L flag to highligh lines in alternating bg color 2023-11-21 11:40:55 +01:00
T.v.Dein
9eadb941da Release v1.0.17 (#9)
* add shortcut -H to --no-headers, it's too cumbersome to type
* added fuzzy search support
* Added basic lisp plugin facilities
* Lisp plugin Addidions:
- added process hook facilities
- added working example lisp plugin for filter hook
- load-path can now be a file as well
- added a couple of lisp helper functions (atoi, split), more may
  follow, see lisplib.go
* linting fixes
2023-10-02 18:15:41 +02:00
T.v.Dein
93800f81c1 release v1.0.16 (#8)
* add shortcut -H to --no-headers, it's too cumbersome to type

* bump version

* add -H to usage

* re-generated

---------

Co-authored-by: Thomas von Dein <tom@vondein.org>
2023-05-03 18:31:28 +02:00
T.v.Dein
a94a4fd5b0 Merge pull request #7 from TLINDEN/development
added --no-headers flag to disable header display in tabular modes
2023-04-21 10:01:19 +02:00
1acbdbc674 added --no-headers flag to disable header display in tabular modes 2023-04-21 09:52:05 +02:00
195f685584 fix release maker 2023-01-23 13:40:50 +01:00
b72a99748f upd Changelog, bump version 2023-01-23 12:38:59 +01:00
3cf9310ef7 -D could not be used together with -a 2023-01-20 13:37:06 +01:00
ceae80c91c fix invalid arg handling (io, stdin) and add tests for this 2023-01-09 12:54:45 +01:00
54add2c801 only upd patch version if any 2022-11-04 20:27:22 +01:00
2d157bf2c0 force uniseg release version, see actions#3396457307/ 2022-11-04 20:22:51 +01:00
6f71a028f0 added licenses 2022-11-04 20:22:40 +01:00
dfc7c2e03e upd dep licenses, upd go modules 2022-11-04 20:10:54 +01:00
c443914222 fix spacing mess 2022-11-03 20:17:02 +01:00
eddd4e4180 add feature request template 2022-11-03 20:12:26 +01:00
0d05505493 Merge branch 'main' into development 2022-11-03 20:09:10 +01:00
T.v.Dein
a461dba10d Merge pull request #6 from TLINDEN/issue-template
Update issue templates
2022-11-03 20:08:42 +01:00
T.v.Dein
ca71f8a572 Update issue templates 2022-11-03 19:59:19 +01:00
60230eb1f6 added show-version target 2022-11-03 19:51:42 +01:00
315e8d5363 fix changelog 2022-11-03 19:30:55 +01:00
88d078a535 fix to be able to run 'make' on systems w/o perl 2022-11-03 19:26:57 +01:00
74ab3a1804 version bump 2022-11-03 13:16:02 +01:00
2d8614fa0f demo gif 2022-11-03 13:11:09 +01:00
c8bad4df1a check completion errors 2022-11-02 14:33:48 +01:00
335b2665f2 turned completion subcommand into option 2022-11-01 11:40:36 +01:00
8552270a68 added completion support 2022-10-31 16:19:12 +01:00
6f49b76607 added demo generator 2022-10-27 19:24:22 +02:00
4653eaca09 added demo generator 2022-10-27 19:23:48 +02:00
722eea7e7b add asciicast demo 2022-10-27 19:22:36 +02:00
304f2182ac js doesnt work, try image 2022-10-27 19:18:09 +02:00
73908b1661 added ascii cast 2022-10-27 19:16:41 +02:00
105ba96757 -A was not implemented, oops! 2022-10-27 18:38:46 +02:00
0681f67bc6 fix release link 2022-10-26 12:38:08 +02:00
066ddd0d98 re-organized pattern matching code 2022-10-25 18:34:28 +02:00
417faf3ff2 fixed #5, colorization now always works as expected 2022-10-25 14:24:05 +02:00
001021dac8 Workaround for issue#3: text containing tag like content is not colorized properly. 2022-10-24 17:55:31 +02:00
5c42f7ab9a check pattern on startup 2022-10-24 14:49:23 +02:00
5e65726cb0 cleanup 2022-10-24 14:05:50 +02:00
138ae51936 added CSV output mode, enhanced parser tests 2022-10-23 16:57:30 +02:00
b5c802403b added CSV parsing support, enabled with -s 2022-10-22 12:27:33 +02:00
e54435c2e4 added support for environment variables 2022-10-22 10:21:39 +02:00
975510c86a using enum modeflags, use my own usage template, generated from manpage so I don't have to maintain it twice, it's also nicer 2022-10-21 10:21:07 +02:00
9dd2a49d9b adapted version generation to cfg module, added and fixed unit tests 2022-10-19 19:32:41 +02:00
90872e0c60 fix linter errors 2022-10-19 12:57:50 +02:00
baac74eb47 yaml fix 2022-10-19 12:51:54 +02:00
360dd28e20 add linter 2022-10-19 12:50:20 +02:00
1e36c148ff get rid of global variables, makes testing way easier 2022-10-19 12:44:19 +02:00
399620de98 Added so we can use struct holding all configuration 2022-10-19 12:43:54 +02:00
5d10875a3f fix pointer bug, mockdata have been overwritten by go test everytime,
now use a const struct via func.
2022-10-18 19:45:09 +02:00
4481f59eda no need to return string when using io.Writer anyway 2022-10-17 23:40:53 +02:00
1b2f51dcaf Changed print funcs to use an io.Writer, reimplemented print tests 2022-10-17 20:04:05 +02:00
0d6de3fe5b add-fixes 2022-10-16 19:48:12 +02:00
ec23ae2e76 fix todo 2022-10-16 19:45:46 +02:00
76930ab45a added yaml output mode support (-o yaml or -Y) 2022-10-16 19:44:26 +02:00
a77e4dbc5a added NumberizeHeaders() unit test 2022-10-16 16:37:47 +02:00
9305f48639 added target to execute a single unit test manually 2022-10-16 16:37:26 +02:00
da276a1b50 replaced github.com/xhit/go-str2duration with my own func + tests 2022-10-16 15:30:34 +02:00
dfd3ab9b77 fixed version generation 2022-10-15 19:46:03 +02:00
28 changed files with 234 additions and 3003 deletions

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

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

View File

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

10
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"

View File

@@ -1,25 +0,0 @@
name: build-and-test-tablizer
on: [push, pull_request]
jobs:
build:
strategy:
matrix:
version: [1.17, 1.18, 1.19]
os: [ubuntu-latest, windows-latest, macos-latest]
name: Build
runs-on: ${{ matrix.os }}
steps:
- name: Set up Go 1.18
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.version }}
id: go
- name: checkout
uses: actions/checkout@v3
- name: build
run: make
- name: test
run: make test

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
releases
tablizer
*.out

View File

@@ -1,179 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org).
## [v1.0.10](https://github.com/TLINDEN/tablizer/tree/v1.0.10) - 2022-10-15
### Added
- Added various sort modes: sort by time, by duration, numerical (-a -t -i)
- Added possibility to modify sort order to descending (-D)
- Added support to specify a regexp in column selector -c, which can
also be mixed with numerical column spec
- More unit tests
### Fixed
- Column specification allowed to specify duplicate columns like `-c
1,2,1,2` unchecked. Now this list will be deduplicated before use.
## [v1.0.9](https://github.com/TLINDEN/tablizer/tree/v1.0.9) - 2022-10-14
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.8...v1.0.9)
### Added
- Added Changelog, Contribution guidelines and no COC.
### Changed
- some minor changes to satisfy linter.
## [v1.0.8](https://github.com/TLINDEN/tablizer/tree/v1.0.8) - 2022-10-13
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.7...v1.0.8)
### Added
- Added sort support with the new parameter -k (like sort(1).
## [v1.0.7](https://github.com/TLINDEN/tablizer/tree/v1.0.7) - 2022-10-11
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.6...v1.0.7)
### Added
- Added pattern highlighting support.
- Added more unit tests.
### Fixed
- Fixed extended more output in combination with -c.
- Fixed issue #4, the version string was missing.
## [v1.0.6](https://github.com/TLINDEN/tablizer/tree/v1.0.6) - 2022-10-05
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.5...v1.0.6)
### Added
- Added documentation about regexp syntax in the manpage.
- Added more unit tests.
### Changed
- Rewrote the input parser.
- Some more refactoring work has been done.
## [v1.0.5](https://github.com/TLINDEN/tablizer/tree/v1.0.5) - 2022-10-05
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.4...v1.0.5)
### Added
- A new option has been added: --invert-match -v which behaves like
the same option in grep(1): it inverts the pattern match.
- A few more unit tests have been added.
### Fixed
- Pattern matching did not work, because the (new) help subcommand
lead to cobra taking care of the first arg to the program
(argv[1]). So now there's a new parameter -m which displays the
manpage and no more subcommands.
## [v1.0.4](https://github.com/TLINDEN/tablizer/tree/v1.0.4) - 2022-10-04
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.3...v1.0.4)
### Added
- Development version of the compiled binary now uses git vars
in addition to program version.
- Added an option to display the manual page (compiled in) as text:
--help, for cases where a user just installed the binary.
### Changed
- Fixed go module namespace.
## [v1.0.3](https://github.com/TLINDEN/tablizer/tree/v1.0.3) - 2022-10-03
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.2...v1.0.3)
### Added
- Added a new output mode: shell mode, which allows the user
to use the output in a shell eval loop to further process
the data.
### Changed
- More refactoring work has been done.
## [v1.0.2](https://github.com/TLINDEN/tablizer/tree/v1.0.2) - 2022-10-02
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.1...v1.0.2)
### Added
- Added some basic unit tests.
### Changed
- Code has been refactored to be more efficient.
- Replaced table generation code with Tablewriter.
## [v1.0.1](https://github.com/TLINDEN/tablizer/tree/v1.0.1) - 2022-09-30
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/v1.0.0...v1.0.1)
### Added
- Added a unix manual page.
- Added release builder to Makefile
### Changed
- Various minor fixes.
## [v1.0.0](https://github.com/TLINDEN/tablizer/tree/v1.0.0) - 2022-09-28
[Full Changelog](https://github.com/TLINDEN/tablizer/compare/02a64a5c3fe4220df2c791ff1421d16ebd428c19...v1.0.0)
Initial release.

View File

@@ -1,113 +0,0 @@
# No Code of Conduct
*TL;DR:* This project does **NOT** have a so called Code of Conduct,
nor will it ever have one.
## The Rant
The reasons are somewhat complicated and I'll try my best to document
them here.
Ethical codes or rules come along like laws. But how is ethical or
moral behavior defined? And who defines which behavior is ethical and
which is not? Certainly not me.
Unless you live in a dictatorship (and more than half of the
population of planet earth do as of this writing), laws come into
existence by democratic procedures. Laws cover almost every aspect of
live in a society. Laws allow and forbid behavior and laws sanction
infringements.
A software project like this one on the other hand is not a society.
There are not enough people involved to form democratic
structures. And there will always be a minority of users who have the
right to commit or reject code. How could any maintainer of a software
project dare to decree rules upon others? Actually, am I, the current
maintainer of this very project authorized to do so?
I think the anser to this question clearly is NO.
The issue is being complicated by the fact, that open source
development these days happens on planetary scale. And this planet
houses hundreds if not thousands of different cultures, philosophies,
ideologies and worldviews. The answer to many ethical questions will
in most cases vague and nebulous.
Ones joke will always be another ones insult.
Then there is the problem of language. I myself am not an english
native, but I publish everyting using the english language. I am able
to communicate with most people in the open source community because
of that. But I am certainly not able to understand everything and
everyone. There might be nuances to a sentence I don't sense, there
might be sarcastic connotations I don't understand or references to
historical figures, events or traditions I don't know and never have
heard of.
Judging over other peoples online behavior looks like a titanic task
to me. It is just not my job to judge others, I am not legitimized or
authorized to do so and I am not interested in this kind of business.
Another huge problem with ethical rules is that you need to outline
and enforce sanctions on those who violate the rules. But since I am
not an elected authority how would I be able to do this? I don't
know. And what happens if someone complains about myself? Shall I
remove myself from my own project? Come on!
Last but not least there's the law. So, let's say someone in india
says something insulting to some other developer in an issue. Of
course german law does not apply to indian people. More, the insult
might actually not be an insult in india. In the end, nothing would
happen. Under normal circumstances, maintainers would delete the
posting, ban the user or remove push privileges etc.
But then, is there a way for the offending user to defend himself? Of
course not, since neither indian or german law alone applies. I cannot
go to a german court and sue the guy and he cannot do the same in
india. Or - we possibly could but the judges on both countries would
just laugh and close the case.
That being said, I don't have the power nor the tools, nor the
authority to enforce serious sanctions of any meaningful kind against
others. Therefore I cannot outline any rules whatsoever.
And let's not even start talking about there undemocratic "comitees"
many projects are forming to circumvent this problem. Some projects
even include external entities like a lawer or some bureaucrat
somewhere just to have the ability to complain against a comitee
member. What a mess!
## So, which are the ethical rules within this project then?
Well, there are none.
This project is about code, not society. It doesn't matter where you
come from, how you look, how you think, what you believe, who your
friends are, whay you said or did sometime in the past. I don't even
care if you are a human being. You are an alien so bored that you need
to submit code on github? Fine with me. You're a convicted criminal? I
don't give a shit!
**The only thing I am interested here is Code and only Code.**
So if anyhing happens here I don't like or I am obliged by law to act
on, I will decide on a case to case basis what to do. And
unfortunately, since this is the nature of a github project, you
cannot complain, object or protest. I am very sorry!
If you will, let's at least outline these:
- Please - just please - behave towards others as you'd expect others
to behave towards yourself.
- Don't judge others for any reason.
- Only judge the code.
But these are not rules, only a friendly appeal to you as a developer
and user.
Thanks a lot!

View File

@@ -1,94 +0,0 @@
## Project Goals
The goal of this project is to build a small tool which helps in day
to day work with tabular output of various commandline programs. It
should be small, fast and easy to understand. The idea is to replace
multiline shell pipes using awk, sed and grep with just one
binary.
There will be no GUI, no web interface, no public API of some sort, no
builtin interpreter.
The programming language used for this project will always be
[GOLANG](https://go.dev/) with the exception of the documentation
([Perl POD](https://perldoc.perl.org/perlpod)) and the Makefile.
# Contributing
You can contribute to this project in various ways:
## Open an issue
If you encounter a problem or don't understand how the program works
or if you think the documentation is unclear, please don't hesitate to
open an issue.
Please add as much information about the case as possible, such as:
- Your environment (operating system etc)
- tablizer version (`tablizer --version`)
- Input data. Please replace sensitive information with mock data!
- Actual program output.
- Expected program output.
- Error message - if any.
Be aware that I am working on this (and some other) project in my
spare time which is scarce. Therefore please don't expect me to
respond to your query within hours or even days. Be patient, but I
WILL respond.
## Pull Requests
Code and documentation help is always much appreciated! Please follow
thes guidelines to successfully contribute:
- Every pull request shall be based on latest `development`
branch. `main` is only used for releases.
- Execute the unit tests before committing: `make test`. There shall
be no errors.
- Strive to be backwards compatible so that users who are already
using the program don't have to change their habits - unless it is
really neccessary.
- Try to add a unit test for your addition.
- Don't ever change existing unit tests!
- Add a meaningful and comprehensive rationale about your contribution:
- Why do you think it might be useful for others?
- What did you actually change or add?
- Is there an open issue which this PR fixes and if so, please link
to that issue.
- [Re-]format your code with `gofmt -s`.
- Avoid unneccesary dependencies, especially for very small functions.
- **If** a new dependency is being added, it must be compatible with
our [license agreement](LICENSE).
- You need to accept that the code or documentation you contribute
will be redistributed under the terms of said license agreement. If
your contribution is considerably large or if you contribute
regularly, then feel free to add your name and if you want your
email address to the *AUTHORS* section of the
[manpage](tablizer.pod).
- Adhere to the above mentioned project goals.
- If you are unsure if your addition or change will be accepted,
better ask before starting coding. Open an issue about your proposal
and let's discuss it! That way we avoid doing unnessesary work on
both sides.
Each pull request will be carefully reviewed and if it is a useful
addition it will be accepted. However, please be prepared that
sometimes a PR will be rejected. The reasons may vary and will be
documented. Perhaps the above guidelines are not matched, or the
addition seems to be not so useful from my perspective, maybe there
are too much changes or there might be changes I don't even
understand.
But whatever happens: your contribution is always welcome!

View File

@@ -1,61 +0,0 @@
# Copyright © 2022 Thomas von Dein
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# no need to modify anything below
tool = tablizer
version = $(shell egrep "= .v" lib/common.go | cut -d'=' -f2 | cut -d'"' -f 2)
archs = android darwin freebsd linux netbsd openbsd windows
PREFIX = /usr/local
UID = root
GID = 0
BRANCH = $(shell git describe --all | cut -d/ -f2)
COMMIT = $(shell git rev-parse --short=8 HEAD)
BUILD = $(shell date +%Y.%m.%d.%H%M%S)
VERSION:= $(if $(filter $(BRANCH), development),$(version)-$(BRANCH)-$(COMMIT)-$(BUILD),$(version))
all: $(tool).1 cmd/$(tool).go buildlocal
%.1: %.pod
pod2man -c "User Commands" -r 1 -s 1 $*.pod > $*.1
cmd/%.go: %.pod
echo "package cmd" > cmd/$*.go
echo >> cmd/$*.go
echo "var manpage = \`" >> cmd/$*.go
pod2text $*.pod >> cmd/$*.go
echo "\`" >> cmd/$*.go
buildlocal:
go build -ldflags "-X 'github.com/tlinden/tablizer/lib.VERSION=$(VERSION)'"
release:
./mkrel.sh $(tool) $(version)
gh release create $(version) --generate-notes releases/*
install: buildlocal
install -d -o $(UID) -g $(GID) $(PREFIX)/bin
install -d -o $(UID) -g $(GID) $(PREFIX)/man/man1
install -o $(UID) -g $(GID) -m 555 $(tool) $(PREFIX)/sbin/
install -o $(UID) -g $(GID) -m 444 $(tool).1 $(PREFIX)/man/man1/
clean:
rm -rf $(tool) releases
test:
go test -v ./...

181
README.md
View File

@@ -1,12 +1,93 @@
[![Actions](https://github.com/tlinden/tablizer/actions/workflows/ci.yaml/badge.svg)](https://github.com/tlinden/tablizer/actions)
[![License](https://img.shields.io/badge/license-GPL-blue.svg)](https://github.com/tlinden/tablizer/blob/master/LICENSE)
[![Go Report Card](https://goreportcard.com/badge/github.com/tlinden/tablizer)](https://goreportcard.com/report/github.com/tlinden/tablizer)
[![status-badge](https://ci.codeberg.org/api/badges/15519/status.svg)](https://ci.codeberg.org/repos/15519)
[![License](https://img.shields.io/badge/license-GPL-blue.svg)](https://codeberg.org/scip/tablizer/blob/master/LICENSE)
[![Go Report Card](https://goreportcard.com/badge/codeberg.org/scip/tablizer)](https://goreportcard.com/report/codeberg.org/scip/tablizer)
## tablizer - Manipulate tabular output of other programs
> [!IMPORTANT]
> This software is now being maintained on [Codeberg](https://codeberg.org/scip/tablizer/).
Tablizer can be used to re-format tabular output of other
programs. While you could do this using standard unix tools, in some
cases it's a hard job.
cases it's a hard job. With tablizer you can filter by column[s],
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.
## FEATURES
- supports csv, json or ascii format input from files or stdin
- split any tabular input data by character or regular expression into columns
- add headers if input data doesn't contain them (automatically or manually)
- print tabular data as ascii table, org-mode, markdown, csv, shell-evaluable or yaml format
- filter rows by regular expression (saves a call to `| grep ...`)
- filter rows by column filter
- filters may also be negations eg `-Fname!=cow.*` or `-v`
- modify cells wih regular expressions
- reduce columns by specifying which columns to show, with regex support
- color support
- sort by any field[s], multiple sort modes are supported
- shell completion for options
- regular used options can be put into a config file
- filter TUI where where you can interactively sort and filter rows
## Demo
![demo cast](vhsdemo/demo.gif)
## 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
-j, --json Read JSON input (must be array of hashes)
-I, --interactive Interactively filter and select rows
--auto-headers Generate headers if there are none present in input
--custom-headers a,b,... Use custom headers, separated by comma
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
--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
Other Flags:
-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:
```
@@ -22,13 +103,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
@@ -66,17 +147,55 @@ 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 -Fname=2
NAME READY STATUS RESTARTS AGE
repldepl-7bcd8d5b64-q2bf4 1/1 Running 1 (69m ago) 5h26m
```
Here we filtered the `NAME` column for `2`, which would have matched
otherwise on all rows.
There are more output modes like org-mode (orgtbl) and markdown.
You can also use it to modify certain cells using regular expression
matching. For example:
```shell
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
repldepl-7bcd8d5b64-q2bf4 1/1 Running 1-(69m-ago) 5h26m
```
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" />
## Installation
There are multiple ways to install **tablizer**:
- Go to the [latest release page](https://github.com/muesli/mango/releases/latest),
- You can use [stew](https://github.com/marwanhawari/stew) to install tablizer:
```default
stew install tlinden/tablizer
```
- Go to the [latest release page](https://codeberg.org/scip/tablizer/releases),
locate the binary for your operating system and platform.
Download it and put it into some directory within your `$PATH` variable.
@@ -89,7 +208,7 @@ There are multiple ways to install **tablizer**:
- You can also install from source. Issue the following commands in your shell:
```
git clone https://github.com/TLINDEN/tablizer.git
git clone https://codeberg.org/scip/tablizer.git
cd tablizer
make
sudo make install
@@ -101,10 +220,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://codeberg.org/scip/tablizer/raw/branch/tablizer.pod).
Or if you cloned the repository you can read it this way (perl needs
to be installed though): `perldoc tablizer.pod`.
@@ -121,7 +239,42 @@ that's the best way for me to forget to do something.
In order to report a bug, unexpected behavior, feature requests
or to submit a patch, please open an issue on github:
https://github.com/TLINDEN/tablizer/issues.
https://codeberg.org/scip/tablizer/issues.
## Prior Art
When I started with tablizer I was not aware that other tools
exist. Here is a non-exhausive list of the ones I find especially
awesome:
### [miller](https://github.com/johnkerl/miller)
This is a really powerful tool to work with tabular data and it also
allows other inputs as json, csv etc. You can filter, manipulate,
create pipelines, there's even a programming language builtin to do
even more amazing things.
### [csvq](https://github.com/mithrandie/csvq)
Csvq allows you to query CSV and TSV data using SQL queries. How nice
is that? Highly recommended if you have to work with a large (and
wide) dataset and need to apply a complicated set of rules.
### [goawk](https://github.com/benhoyt/goawk)
Goawk is a 100% POSIX compliant AWK implementation in GO, which also
supports CSV and TSV data as input (using `-i csv` for example). You
can apply any kind of awk code to your tabular data, there are no
limit to your creativity!
### [teip](https://github.com/greymd/teip)
I particularly like teip, it's a real gem. You can use it to drill
"holes" into your tabular data and modify these "holes" using small
external unix commands such as grep or sed. The possibilities are
endless, you can even use teip to modify data inside a hole created by
teip. Highly recommended.
## Copyright and license
@@ -133,4 +286,4 @@ T.v.Dein <tom AT vondein DOT org>
## Project homepage
https://github.com/TLINDEN/tablizer
https://codeberg.org/scip/tablizer

11
TODO.md
View File

@@ -1,11 +0,0 @@
## Fixes to be implemented
## Features to be implemented
- add output modes yaml and csv
- add --no-headers option
- add input parsing support for CSV including unquoting of stuff
like: `"xxx","1919 b"` etc, maybe an extra option for unquoting

View File

@@ -1,111 +0,0 @@
/*
Copyright © 2022 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cmd
import (
"bytes"
"fmt"
"github.com/spf13/cobra"
"github.com/tlinden/tablizer/lib"
"log"
"os"
"os/exec"
)
var ShowManual = false
func man() {
man := exec.Command("less", "-")
var b bytes.Buffer
b.Write([]byte(manpage))
man.Stdout = os.Stdout
man.Stdin = &b
man.Stderr = os.Stderr
err := man.Run()
if err != nil {
log.Fatal(err)
}
}
var rootCmd = &cobra.Command{
Use: "tablizer [regex] [file, ...]",
Short: "[Re-]tabularize tabular data",
Long: `Manipulate tabular output of other programs`,
RunE: func(cmd *cobra.Command, args []string) error {
if lib.ShowVersion {
fmt.Printf("This is tablizer version %s\n", lib.VERSION)
return nil
}
if ShowManual {
man()
return nil
}
err := lib.PrepareModeFlags()
if err != nil {
return err
}
lib.PrepareSortFlags()
return lib.ProcessFiles(args)
},
}
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
rootCmd.PersistentFlags().BoolVarP(&lib.Debug, "debug", "d", false, "Enable debugging")
rootCmd.PersistentFlags().BoolVarP(&lib.NoNumbering, "no-numbering", "n", false, "Disable header numbering")
rootCmd.PersistentFlags().BoolVarP(&lib.NoColor, "no-color", "N", false, "Disable pattern highlighting")
rootCmd.PersistentFlags().BoolVarP(&lib.ShowVersion, "version", "V", false, "Print program version")
rootCmd.PersistentFlags().BoolVarP(&lib.InvertMatch, "invert-match", "v", false, "select non-matching rows")
rootCmd.PersistentFlags().BoolVarP(&ShowManual, "man", "m", false, "Display manual page")
rootCmd.PersistentFlags().StringVarP(&lib.Separator, "separator", "s", lib.DefaultSeparator, "Custom field separator")
rootCmd.PersistentFlags().StringVarP(&lib.Columns, "columns", "c", "", "Only show the speficied columns (separated by ,)")
// sort options
rootCmd.PersistentFlags().IntVarP(&lib.SortByColumn, "sort-by", "k", 0, "Sort by column (default: 1)")
rootCmd.PersistentFlags().BoolVarP(&lib.SortDescending, "sort-desc", "D", false, "Sort in descending order (default: ascending)")
rootCmd.PersistentFlags().BoolVarP(&lib.SortNumeric, "sort-numeric", "i", false, "sort according to string numerical value")
rootCmd.PersistentFlags().BoolVarP(&lib.SortTime, "sort-time", "t", false, "sort according to time string")
rootCmd.PersistentFlags().BoolVarP(&lib.SortAge, "sort-age", "a", false, "sort according to age (duration) string")
// output flags, only 1 allowed, hidden, since just short cuts
rootCmd.PersistentFlags().BoolVarP(&lib.OutflagExtended, "extended", "X", false, "Enable extended output")
rootCmd.PersistentFlags().BoolVarP(&lib.OutflagMarkdown, "markdown", "M", false, "Enable markdown table output")
rootCmd.PersistentFlags().BoolVarP(&lib.OutflagOrgtable, "orgtbl", "O", false, "Enable org-mode table output")
rootCmd.PersistentFlags().BoolVarP(&lib.OutflagShell, "shell", "S", false, "Enable shell mode output")
rootCmd.MarkFlagsMutuallyExclusive("extended", "markdown", "orgtbl", "shell")
rootCmd.Flags().MarkHidden("extended")
rootCmd.Flags().MarkHidden("orgtbl")
rootCmd.Flags().MarkHidden("markdown")
rootCmd.Flags().MarkHidden("shell")
// same thing but more common, takes precedence over above group
rootCmd.PersistentFlags().StringVarP(&lib.OutputMode, "output", "o", "", "Output mode - one of: orgtbl, markdown, extended, shell, ascii(default)")
}

View File

@@ -1,206 +0,0 @@
package cmd
var manpage = `
NAME
tablizer - Manipulate tabular output of other programs
SYNOPSIS
Usage:
tablizer [regex] [file, ...] [flags]
Flags:
-c, --columns string Only show the speficied columns (separated by ,)
-d, --debug Enable debugging
-h, --help help for tablizer
-v, --invert-match select non-matching rows
-m, --man Display manual page
-n, --no-numbering Disable header numbering
-N, --no-color Disable pattern highlighting
-o, --output string Output mode - one of: orgtbl, markdown, extended, ascii(default)
-X, --extended Enable extended output
-M, --markdown Enable markdown table output
-O, --orgtbl Enable org-mode table output
-s, --separator string Custom field separator
-a, --sort-age sort according to age (duration) string
-k, --sort-by int Sort by column (default: 1)
-D, --sort-desc Sort in descending order (default: ascending)
-i, --sort-numeric sort according to string numerical value
-t, --sort-time sort according to time string
-v, --version Print program version
DESCRIPTION
Many programs generate tabular output. But sometimes you need to
post-process these tables, you may need to remove one or more columns or
you may want to filter for some pattern (See PATTERNS) or you may need
the output in another program and need to parse it somehow. Standard
unix tools such as awk(1), grep(1) or column(1) may help, but sometimes
it's a tedious business.
Let's take the output of the tool kubectl. It contains cells with
withespace and they do not separate columns by TAB characters. This is
not easy to process.
You can use tablizer to do these and more things.
tablizer analyses the header fields of a table, registers the column
positions of each header field and separates columns by those positions.
Without any options it reads its input from "STDIN", but you can also
specify a file as a parameter. If you want to reduce the output by some
regular expression, just specify it as its first parameter. You may also
use the -v option to exclude all rows which match the pattern. Hence:
# read from STDIN
kubectl get pods | tablizer
# read a file
tablizer filename
# search for pattern in a file (works like grep)
tablizer regex 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.:
NAME(1) READY(2) STATUS(3) RESTARTS(4) AGE(5)
These numbers denote the column and you can use them to specify which
columns you want to have in your output (see COLUMNS:
kubectl get pods | tablizer -c1,3
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.
By default, if a pattern has been speficied, matches will be
highlighted. You can disable this behavior with the -N option.
Use the -k option to specify by which column to sort the tabular data
(as in GNU sort(1)). The default sort column is the first one. To
disable sorting at all, supply 0 (Zero) to -k. The default sort order is
ascending. You can change this to descending order using the option -D.
The default sort order is by string, but there are other sort modes:
-a --sort-age
Sorts duration strings like "1d4h32m51s".
-i --sort-numeric
Sorts numeric fields.
-t --sort-time
Sorts timestamps.
Finally the -d option enables debugging output which is mostly useful
for the developer.
PATTERNS
You can reduce the rows being displayed by using a regular expression
pattern. The regexp is PCRE compatible, refer to the syntax cheat sheet
here: <https://github.com/google/re2/wiki/Syntax>. If you want to read a
more comprehensive documentation about the topic and have perl installed
you can read it with:
perldoc perlre
Or read it online: <https://perldoc.perl.org/perlre>.
A note on modifiers: the regexp engine used in tablizer uses another
modifier syntax:
(?MODIFIER)
The most important modifiers are:
"i" ignore case "m" multiline mode "s" single line mode
Example for a case insensitive search:
kubectl get pods -A | tablizer "(?i)account"
COLUMNS
The parameter -c can be used to specify, which columns to display. By
default tablizer numerizes the header names and these numbers can be
used to specify which header to display, see example above.
However, beside numbers, you can also use regular expressions with -c,
also separated by comma. And you can mix column numbers with regexps.
Lets take this table:
PID TTY TIME CMD
14001 pts/0 00:00:00 bash
42871 pts/0 00:00:00 ps
42872 pts/0 00:00:00 sed
We want to see only the CMD column and use a regex for this:
ps | tablizer -s '\s+' -c C
CMD(4)
bash
ps
tablizer
sed
where "C" is our regexp which matches CMD.
OUTPUT MODES
There might be cases when the tabular output of a program is way too
large for your current terminal but you still need to see every column.
In such cases the -o extended or -X option can be useful which enables
*extended mode*. In this mode, each row will be printed vertically,
header left, value right, aligned by the field widths. Here's an
example:
kubectl get pods | ./tablizer -o extended
NAME: repldepl-7bcd8d5b64-7zq4l
READY: 1/1
STATUS: Running
RESTARTS: 1 (71m ago)
AGE: 5h28m
You can of course still use a regex to reduce the number of rows
displayed.
The option -o shell can be used if the output has to be processed by the
shell, it prints variable assignments for each cell, one line per row:
kubectl get pods | ./tablizer -o extended ./tablizer -o shell
NAME="repldepl-7bcd8d5b64-7zq4l" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h"
NAME="repldepl-7bcd8d5b64-m48n8" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h"
NAME="repldepl-7bcd8d5b64-q2bf4" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h"
You can use this in an eval loop.
Beside normal ascii mode (the default) and extended mode there are more
output modes available: orgtbl which prints an Emacs org-mode table and
markdown which prints a Markdown table.
BUGS
In order to report a bug, unexpected behavior, feature requests or to
submit a patch, please open an issue on github:
<https://github.com/TLINDEN/tablizer/issues>.
LICENSE
This software is licensed under the GNU GENERAL PUBLIC LICENSE version
3.
Copyright (c) 2022 by Thomas von Dein
This software uses the following GO libraries:
repr (https://github.com/alecthomas/repr)
Released under the MIT License, Copyright (c) 2016 Alec Thomas
cobra (https://github.com/spf13/cobra)
Released under the Apache 2.0 license, Copyright 2013-2022 The Cobra
Authors
AUTHORS
Thomas von Dein tom AT vondein DOT org
`

21
go.mod
View File

@@ -1,21 +0,0 @@
module github.com/tlinden/tablizer
go 1.18
require (
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/gookit/color v1.5.2
github.com/olekukonko/tablewriter v0.0.5
github.com/spf13/cobra v1.5.0
github.com/xhit/go-str2duration/v2 v2.0.0
)
require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/mattn/go-runewidth v0.0.10 // indirect
github.com/rivo/uniseg v0.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 // indirect
)

44
go.sum
View File

@@ -1,44 +0,0 @@
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/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.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/xhit/go-str2duration/v2 v2.0.0 h1:uFtk6FWB375bP7ewQl+/1wBcn840GPhnySOdcz/okPE=
github.com/xhit/go-str2duration/v2 v2.0.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,95 +0,0 @@
/*
Copyright © 2022 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lib
import (
"github.com/gookit/color"
//"github.com/xo/terminfo"
)
var (
// command line flags
Debug bool
XtendedOut bool
NoNumbering bool
ShowVersion bool
Columns string
UseColumns []int
DefaultSeparator string = `(\s\s+|\t)`
Separator string = `(\s\s+|\t)`
OutflagExtended bool
OutflagMarkdown bool
OutflagOrgtable bool
OutflagShell bool
OutputMode string
InvertMatch bool
Pattern string
/*
FIXME: make configurable somehow, config file or ENV
see https://github.com/gookit/color will be set by
io.ProcessFiles() according to currently supported
color mode.
*/
MatchFG string
MatchBG string
NoColor bool
// colors to be used per supported color mode
Colors = map[color.Level]map[string]string{
color.Level16: {
"bg": "green", "fg": "black",
},
color.Level256: {
"bg": "lightGreen", "fg": "black",
},
color.LevelRgb: {
// FIXME: maybe use something nicer
"bg": "lightGreen", "fg": "black",
},
}
// used for validation
validOutputmodes = "(orgtbl|markdown|extended|ascii)"
// main program version
Version = "v1.0.10"
// generated version string, used by -v contains lib.Version on
// main branch, and lib.Version-$branch-$lastcommit-$date on
// development branch
VERSION string
// sorting
SortByColumn int
SortDescending bool
SortNumeric bool
SortTime bool
SortAge bool
SortMode string
)
// contains a whole parsed table
type Tabdata struct {
maxwidthHeader int // longest header
maxwidthPerCol []int // max width per column
columns int // count
headers []string // [ "ID", "NAME", ...]
entries [][]string
}

View File

@@ -1,216 +0,0 @@
/*
Copyright © 2022 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lib
import (
"errors"
"fmt"
"github.com/gookit/color"
"os"
"regexp"
"sort"
"strconv"
"strings"
)
func contains(s []int, e int) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
// parse columns list given with -c
func PrepareColumns(data *Tabdata) error {
UseColumns = nil
if len(Columns) > 0 {
for _, use := range strings.Split(Columns, ",") {
if len(use) == 0 {
msg := fmt.Sprintf("Could not parse columns list %s: empty column", Columns)
return errors.New(msg)
}
usenum, err := strconv.Atoi(use)
if err != nil {
// might be a regexp
colPattern, err := regexp.Compile(use)
if err != nil {
msg := fmt.Sprintf("Could not parse columns list %s: %v", Columns, err)
return errors.New(msg)
}
// find matching header fields
for i, head := range data.headers {
if colPattern.MatchString(head) {
UseColumns = append(UseColumns, i+1)
}
}
} else {
// we digress from go best practises here, because if
// a colum spec is not a number, we process them above
// inside the err handler for atoi(). so only add the
// number, if it's really just a number.
UseColumns = append(UseColumns, usenum)
}
}
// 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))
for _, i := range UseColumns {
imap[i] = 0
}
UseColumns = nil
for k := range imap {
UseColumns = append(UseColumns, k)
}
sort.Ints(UseColumns)
}
return nil
}
// prepare headers: add numbers to headers
func numberizeHeaders(data *Tabdata) {
numberedHeaders := []string{}
maxwidth := 0 // start from scratch, so we only look at displayed column widths
for i, head := range data.headers {
headlen := 0
if len(Columns) > 0 {
// -c specified
if !contains(UseColumns, i+1) {
// ignore this one
continue
}
}
if NoNumbering {
numberedHeaders = append(numberedHeaders, head)
headlen = len(head)
} else {
numhead := fmt.Sprintf("%s(%d)", head, i+1)
headlen = len(numhead)
numberedHeaders = append(numberedHeaders, numhead)
}
if headlen > maxwidth {
maxwidth = headlen
}
}
data.headers = numberedHeaders
if data.maxwidthHeader != maxwidth && maxwidth > 0 {
data.maxwidthHeader = maxwidth
}
}
// exclude columns, if any
func reduceColumns(data *Tabdata) {
if len(Columns) > 0 {
reducedEntries := [][]string{}
var reducedEntry []string
for _, entry := range data.entries {
reducedEntry = nil
for i, value := range entry {
if !contains(UseColumns, i+1) {
continue
}
reducedEntry = append(reducedEntry, value)
}
reducedEntries = append(reducedEntries, reducedEntry)
}
data.entries = reducedEntries
}
}
func PrepareModeFlags() error {
if len(OutputMode) == 0 {
// associate short flags like -X with mode selector
switch {
case OutflagExtended:
OutputMode = "extended"
case OutflagMarkdown:
OutputMode = "markdown"
case OutflagOrgtable:
OutputMode = "orgtbl"
case OutflagShell:
OutputMode = "shell"
NoNumbering = true
default:
OutputMode = "ascii"
}
} else {
r, err := regexp.Compile(validOutputmodes)
if err != nil {
return errors.New("Failed to validate output mode spec!")
}
match := r.MatchString(OutputMode)
if !match {
return errors.New("Invalid output mode!")
}
}
return nil
}
func PrepareSortFlags() {
switch {
case SortNumeric:
SortMode = "numeric"
case SortAge:
SortMode = "duration"
case SortTime:
SortMode = "time"
default:
SortMode = "string"
}
}
func trimRow(row []string) []string {
// FIXME: remove this when we only use Tablewriter and strip in ParseFile()!
var fixedrow []string
for _, cell := range row {
fixedrow = append(fixedrow, strings.TrimSpace(cell))
}
return fixedrow
}
func colorizeData(output string) string {
if len(Pattern) > 0 && !NoColor && color.IsConsole(os.Stdout) {
r := regexp.MustCompile("(" + Pattern + ")")
return r.ReplaceAllString(output, "<bg="+MatchBG+";fg="+MatchFG+">$1</>")
} else {
return output
}
}
func isTerminal(f *os.File) bool {
o, _ := f.Stat()
if (o.Mode() & os.ModeCharDevice) == os.ModeCharDevice {
return true
} else {
return false
}
}

View File

@@ -1,135 +0,0 @@
/*
Copyright © 2022 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lib
import (
"fmt"
"reflect"
"testing"
)
func Testcontains(t *testing.T) {
var tests = []struct {
list []int
search int
want bool
}{
{[]int{1, 2, 3}, 2, true},
{[]int{2, 3, 4}, 5, false},
}
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)
}
})
}
}
func TestPrepareColumns(t *testing.T) {
data := Tabdata{
maxwidthHeader: 5,
maxwidthPerCol: []int{
5,
5,
8,
},
columns: 3,
headers: []string{
"ONE", "TWO", "THREE",
},
entries: [][]string{
{
"2", "3", "4",
},
},
}
var tests = []struct {
input string
exp []int
wanterror bool // expect error
}{
{"1,2,3", []int{1, 2, 3}, false},
{"1,2,", []int{}, true},
{"T", []int{2, 3}, false},
{"T,2,3", []int{2, 3}, false},
}
for _, tt := range tests {
testname := fmt.Sprintf("PrepareColumns-%s-%t", tt.input, tt.wanterror)
t.Run(testname, func(t *testing.T) {
Columns = tt.input
err := PrepareColumns(&data)
if err != nil {
if !tt.wanterror {
t.Errorf("got error: %v", err)
}
} else {
if !reflect.DeepEqual(UseColumns, tt.exp) {
t.Errorf("got: %v, expected: %v", UseColumns, tt.exp)
}
}
})
}
}
func TestReduceColumns(t *testing.T) {
var tests = []struct {
expect [][]string
columns []int
}{
{
expect: [][]string{{"a", "b"}},
columns: []int{1, 2},
},
{
expect: [][]string{{"a", "c"}},
columns: []int{1, 3},
},
{
expect: [][]string{{"a"}},
columns: []int{1},
},
{
expect: [][]string{nil},
columns: []int{4},
},
}
input := [][]string{{"a", "b", "c"}}
Columns = "y" // used as a flag with len(Columns)...
for _, tt := range tests {
testname := fmt.Sprintf("reduce-columns-by-%+v", tt.columns)
t.Run(testname, func(t *testing.T) {
UseColumns = tt.columns
data := Tabdata{entries: input}
reduceColumns(&data)
if !reflect.DeepEqual(data.entries, tt.expect) {
t.Errorf("reduceColumns returned invalid data:\ngot: %+v\nexp: %+v", data.entries, tt.expect)
}
})
}
Columns = "" // reset for other tests
UseColumns = nil
}

View File

@@ -1,99 +0,0 @@
/*
Copyright © 2022 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lib
import (
"errors"
"github.com/gookit/color"
"io"
"os"
)
func ProcessFiles(args []string) error {
fds, pattern, err := determineIO(args)
if !isTerminal(os.Stdout) {
color.Disable()
} else {
level := color.TermColorLevel()
MatchFG = Colors[level]["fg"]
MatchBG = Colors[level]["bg"]
}
if err != nil {
return err
}
for _, fd := range fds {
data, err := parseFile(fd, pattern)
if err != nil {
return err
}
err = PrepareColumns(&data)
if err != nil {
return err
}
printData(&data)
}
return nil
}
func determineIO(args []string) ([]io.Reader, string, error) {
var pattern string
var fds []io.Reader
var havefiles bool
if len(args) > 0 {
// threre were args left, take a look
if _, err := os.Stat(args[0]); err != nil {
// first one is not a file, consider it as regexp and
// shift arg list
pattern = args[0]
Pattern = args[0] // FIXME
args = args[1:]
}
if len(args) > 0 {
// only files
for _, file := range args {
fd, err := os.OpenFile(file, os.O_RDONLY, 0755)
if err != nil {
return nil, "", err
}
fds = append(fds, fd)
}
havefiles = true
}
}
if !havefiles {
stat, _ := os.Stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
fds = append(fds, os.Stdin)
} else {
return nil, "", errors.New("No file specified and nothing to read on stdin!")
}
}
return fds, pattern, nil
}

View File

@@ -1,127 +0,0 @@
/*
Copyright © 2022 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lib
import (
"bufio"
"errors"
"fmt"
"github.com/alecthomas/repr"
"io"
"regexp"
"strings"
)
/*
Parse tabular input.
*/
func parseFile(input io.Reader, pattern string) (Tabdata, error) {
data := Tabdata{}
var scanner *bufio.Scanner
hadFirst := false
separate := regexp.MustCompile(Separator)
patternR, err := regexp.Compile(pattern)
if err != nil {
return data, errors.Unwrap(fmt.Errorf("Regexp pattern %s is invalid: %w", pattern, err))
}
scanner = bufio.NewScanner(input)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
parts := separate.Split(line, -1)
if !hadFirst {
// header processing
data.columns = len(parts)
// if Debug {
// fmt.Println(parts)
// }
// process all header fields
for _, part := range parts {
// if Debug {
// fmt.Printf("Part: <%s>\n", string(line[beg:part[0]]))
//}
// register widest header field
headerlen := len(part)
if headerlen > data.maxwidthHeader {
data.maxwidthHeader = headerlen
}
// register fields data
data.headers = append(data.headers, strings.TrimSpace(part))
// done
hadFirst = true
}
} else {
// data processing
if len(pattern) > 0 {
if patternR.MatchString(line) == InvertMatch {
// by default -v is false, so if a line does NOT
// match the pattern, we will ignore it. However,
// if the user specified -v, the matching is inverted,
// so we ignore all lines, which DO match.
continue
}
}
idx := 0 // we cannot use the header index, because we could exclude columns
values := []string{}
for _, part := range parts {
width := len(strings.TrimSpace(part))
if len(data.maxwidthPerCol)-1 < idx {
data.maxwidthPerCol = append(data.maxwidthPerCol, width)
} else {
if width > data.maxwidthPerCol[idx] {
data.maxwidthPerCol[idx] = width
}
}
// if Debug {
// fmt.Printf("<%s> ", value)
// }
values = append(values, strings.TrimSpace(part))
idx++
}
// fill up missing fields, if any
for i := len(values); i < len(data.headers); i++ {
values = append(values, "")
}
data.entries = append(data.entries, values)
}
}
if scanner.Err() != nil {
return data, errors.Unwrap(fmt.Errorf("Failed to read from io.Reader: %w", scanner.Err()))
}
if Debug {
repr.Print(data)
}
return data, nil
}

View File

@@ -1,150 +0,0 @@
/*
Copyright © 2022 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lib
import (
"fmt"
"reflect"
"strings"
"testing"
)
func TestParser(t *testing.T) {
data := Tabdata{
maxwidthHeader: 5,
maxwidthPerCol: []int{
5, 5, 8,
},
columns: 3,
headers: []string{
"ONE", "TWO", "THREE",
},
entries: [][]string{
{
"asd", "igig", "cxxxncnc",
},
{
"19191", "EDD 1", "X",
},
},
}
table := `ONE TWO THREE
asd igig cxxxncnc
19191 EDD 1 X`
readFd := strings.NewReader(table)
gotdata, err := parseFile(readFd, "")
Separator = DefaultSeparator
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, Regex: %s\nExp: %+v\nGot: %+v\n", Separator, data, gotdata)
}
}
func TestParserPatternmatching(t *testing.T) {
var tests = []struct {
entries [][]string
pattern string
invert bool
}{
{
entries: [][]string{
{
"asd", "igig", "cxxxncnc",
},
},
pattern: "ig",
invert: false,
},
{
entries: [][]string{
{
"19191", "EDD 1", "X",
},
},
pattern: "ig",
invert: true,
},
}
table := `ONE TWO THREE
asd igig cxxxncnc
19191 EDD 1 X`
for _, tt := range tests {
testname := fmt.Sprintf("parse-with-pattern-%s-inverted-%t", tt.pattern, tt.invert)
t.Run(testname, func(t *testing.T) {
InvertMatch = tt.invert
readFd := strings.NewReader(table)
gotdata, err := parseFile(readFd, tt.pattern)
if err != nil {
t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata)
}
if !reflect.DeepEqual(tt.entries, gotdata.entries) {
t.Errorf("Parser returned invalid data (pattern: %s, invert: %t)\nExp: %+v\nGot: %+v\n",
tt.pattern, tt.invert, tt.entries, gotdata.entries)
}
})
}
}
func TestParserIncompleteRows(t *testing.T) {
data := Tabdata{
maxwidthHeader: 5,
maxwidthPerCol: []int{
5, 5, 1,
},
columns: 3,
headers: []string{
"ONE", "TWO", "THREE",
},
entries: [][]string{
{
"asd", "igig", "",
},
{
"19191", "EDD 1", "X",
},
},
}
table := `ONE TWO THREE
asd igig
19191 EDD 1 X`
readFd := strings.NewReader(table)
gotdata, err := parseFile(readFd, "")
Separator = DefaultSeparator
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, Regex: %s\nExp: %+v\nGot: %+v\n",
Separator, data, gotdata)
}
}

View File

@@ -1,183 +0,0 @@
/*
Copyright © 2022 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lib
import (
"fmt"
"github.com/gookit/color"
"github.com/olekukonko/tablewriter"
"regexp"
"strings"
)
func printData(data *Tabdata) {
// some output preparations:
if OutputMode != "shell" {
// not needed in eval string
numberizeHeaders(data)
}
// remove unwanted columns, if any
reduceColumns(data)
// sort the data
sortTable(data, SortByColumn)
switch OutputMode {
case "extended":
printExtendedData(data)
case "ascii":
printAsciiData(data)
case "orgtbl":
printOrgmodeData(data)
case "markdown":
printMarkdownData(data)
case "shell":
printShellData(data)
default:
printAsciiData(data)
}
}
/*
Emacs org-mode compatible table (also orgtbl-mode)
*/
func printOrgmodeData(data *Tabdata) {
tableString := &strings.Builder{}
table := tablewriter.NewWriter(tableString)
table.SetHeader(data.headers)
for _, row := range data.entries {
table.Append(trimRow(row))
}
table.Render()
/* fix output for org-mode (orgtbl)
tableWriter output:
+------+------+
| cell | cell |
+------+------+
Needed for org-mode compatibility:
|------+------|
| cell | cell |
|------+------|
*/
leftR := regexp.MustCompile("(?m)^\\+")
rightR := regexp.MustCompile("\\+(?m)$")
color.Print(
colorizeData(
rightR.ReplaceAllString(
leftR.ReplaceAllString(tableString.String(), "|"), "|")))
}
/*
Markdown table
*/
func printMarkdownData(data *Tabdata) {
tableString := &strings.Builder{}
table := tablewriter.NewWriter(tableString)
table.SetHeader(data.headers)
for _, row := range data.entries {
table.Append(trimRow(row))
}
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
table.SetCenterSeparator("|")
table.Render()
color.Print(colorizeData(tableString.String()))
}
/*
Simple ASCII table without any borders etc, just like the input we expect
*/
func printAsciiData(data *Tabdata) {
tableString := &strings.Builder{}
table := tablewriter.NewWriter(tableString)
table.SetHeader(data.headers)
table.AppendBulk(data.entries)
// for _, row := range data.entries {
// table.Append(trimRow(row))
// }
table.SetAutoWrapText(false)
table.SetAutoFormatHeaders(true)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetCenterSeparator("")
table.SetColumnSeparator("")
table.SetRowSeparator("")
table.SetHeaderLine(false)
table.SetBorder(false)
table.SetTablePadding("\t") // pad with tabs
table.SetNoWhiteSpace(true)
table.Render()
color.Print(colorizeData(tableString.String()))
}
/*
We simulate the \x command of psql (the PostgreSQL client)
*/
func printExtendedData(data *Tabdata) {
// needed for data output
format := fmt.Sprintf("%%%ds: %%s\n", data.maxwidthHeader)
if len(data.entries) > 0 {
for _, entry := range data.entries {
for i, value := range entry {
color.Printf(format, data.headers[i], value)
}
fmt.Println()
}
}
}
/*
Shell output, ready to be eval'd. Just like FreeBSD stat(1)
*/
func printShellData(data *Tabdata) {
if len(data.entries) > 0 {
var idx int
for _, entry := range data.entries {
idx = 0
shentries := []string{}
for i, value := range entry {
if len(Columns) > 0 {
if !contains(UseColumns, i+1) {
continue
}
}
shentries = append(shentries, fmt.Sprintf("%s=\"%s\"", data.headers[idx], value))
idx++
}
fmt.Println(strings.Join(shentries, " "))
}
}
}

View File

@@ -1,337 +0,0 @@
/*
Copyright © 2022 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lib
import (
"fmt"
"github.com/gookit/color"
"os"
"strings"
"testing"
)
func stdout2pipe(t *testing.T) (*os.File, *os.File) {
reader, writer, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
origStdout := os.Stdout
os.Stdout = writer
// we need to tell the color mode the io.Writer, even if we don't usw colorization
color.SetOutput(writer)
return origStdout, reader
}
func TestPrinter(t *testing.T) {
startdata := Tabdata{
maxwidthHeader: 5,
maxwidthPerCol: []int{
5,
5,
8,
},
columns: 3,
headers: []string{
"ONE", "TWO", "THREE",
},
entries: [][]string{
{
"asd", "igig", "cxxxncnc",
},
{
"19191", "EDD 1", "X",
},
},
}
expects := map[string]string{
"ascii": `ONE(1) TWO(2) THREE(3)
asd igig cxxxncnc
19191 EDD 1 X`,
"orgtbl": `|--------+--------+----------|
| ONE(1) | TWO(2) | THREE(3) |
|--------+--------+----------|
| asd | igig | cxxxncnc |
| 19191 | EDD 1 | X |
|--------+--------+----------|`,
"markdown": `| ONE(1) | TWO(2) | THREE(3) |
|--------|--------|----------|
| asd | igig | cxxxncnc |
| 19191 | EDD 1 | X |`,
"shell": `ONE="asd" TWO="igig" THREE="cxxxncnc"
ONE="19191" TWO="EDD 1" THREE="X"`,
"extended": `ONE(1): asd
TWO(2): igig
THREE(3): cxxxncnc
ONE(1): 19191
TWO(2): EDD 1
THREE(3): X`,
}
NoColor = true
SortByColumn = 0 // disable sorting
origStdout, reader := stdout2pipe(t)
for mode, expect := range expects {
testname := fmt.Sprintf("print-%s", mode)
t.Run(testname, func(t *testing.T) {
OutputMode = mode
// we need to reset our mock data, since it's being
// modified in printData()
data := startdata
printData(&data)
buf := make([]byte, 1024)
n, err := reader.Read(buf)
if err != nil {
t.Fatal(err)
}
buf = buf[:n]
output := strings.TrimSpace(string(buf))
if output != expect {
t.Errorf("output mode: %s, got:\n%s\nwant:\n%s\n (%d <=> %d)",
mode, output, expect, len(output), len(expect))
}
})
}
// Restore
os.Stdout = origStdout
}
func TestSortPrinter(t *testing.T) {
startdata := Tabdata{
maxwidthHeader: 5,
maxwidthPerCol: []int{
3,
3,
2,
},
columns: 3,
headers: []string{
"ONE", "TWO", "THREE",
},
entries: [][]string{
{
"abc", "345", "b1",
},
{
"bcd", "234", "a2",
},
{
"cde", "123", "c3",
},
},
}
var tests = []struct {
data Tabdata
sortby int
desc bool
expect string
}{
{
data: startdata,
sortby: 1,
desc: false,
expect: `ONE(1) TWO(2) THREE(3)
abc 345 b1
bcd 234 a2
cde 123 c3`,
},
{
data: startdata,
sortby: 2,
desc: false,
expect: `ONE(1) TWO(2) THREE(3)
cde 123 c3
bcd 234 a2
abc 345 b1`,
},
{
data: startdata,
sortby: 3,
desc: false,
expect: `ONE(1) TWO(2) THREE(3)
bcd 234 a2
abc 345 b1
cde 123 c3`,
},
{
data: startdata,
sortby: 1,
desc: true,
expect: `ONE(1) TWO(2) THREE(3)
cde 123 c3
bcd 234 a2
abc 345 b1`,
},
}
NoColor = true
OutputMode = "ascii"
origStdout, reader := stdout2pipe(t)
for _, tt := range tests {
testname := fmt.Sprintf("print-sorted-table-by-column-%d-desc-%t",
tt.sortby, tt.desc)
t.Run(testname, func(t *testing.T) {
SortByColumn = tt.sortby
SortDescending = tt.desc
printData(&tt.data)
buf := make([]byte, 1024)
n, err := reader.Read(buf)
if err != nil {
t.Fatal(err)
}
buf = buf[:n]
output := strings.TrimSpace(string(buf))
if output != tt.expect {
t.Errorf("sort column: %d, got:\n%s\nwant:\n%s",
tt.sortby, output, tt.expect)
}
})
}
// Restore
os.Stdout = origStdout
}
func TestSortByPrinter(t *testing.T) {
data := Tabdata{
maxwidthHeader: 8,
maxwidthPerCol: []int{
5,
9,
3,
26,
},
columns: 4,
headers: []string{
"NAME",
"DURATION",
"COUNT",
"WHEN",
},
entries: [][]string{
{
"beta",
"1d10h5m1s",
"33",
"3/1/2014",
},
{
"alpha",
"4h35m",
"170",
"2013-Feb-03",
},
{
"ceta",
"33d12h",
"9",
"06/Jan/2008 15:04:05 -0700",
},
},
}
var tests = []struct {
sortby string
column int
desc bool
expect string
}{
{
column: 3,
sortby: "numeric",
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`,
},
{
column: 2,
sortby: "duration",
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`,
},
{
column: 4,
sortby: "time",
desc: false,
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`,
},
}
NoColor = true
OutputMode = "ascii"
origStdout, reader := stdout2pipe(t)
for _, tt := range tests {
testname := fmt.Sprintf("print-sorted-table-by-column-%d-desc-%t-sort-by-%s",
tt.column, tt.desc, tt.sortby)
t.Run(testname, func(t *testing.T) {
SortByColumn = tt.column
SortDescending = tt.desc
SortMode = tt.sortby
testdata := data
printData(&testdata)
buf := make([]byte, 1024)
n, err := reader.Read(buf)
if err != nil {
t.Fatal(err)
}
buf = buf[:n]
output := strings.TrimSpace(string(buf))
if output != tt.expect {
t.Errorf("sort column: %d, sortby: %s, got:\n%s\nwant:\n%s",
tt.column, tt.sortby, output, tt.expect)
}
})
}
// Restore
os.Stdout = origStdout
}

View File

@@ -1,88 +0,0 @@
/*
Copyright © 2022 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lib
import (
"github.com/araddon/dateparse"
str2duration "github.com/xhit/go-str2duration/v2"
"sort"
"strconv"
)
func sortTable(data *Tabdata, col int) {
if col <= 0 {
// no sorting wanted
return
}
col-- // ui starts counting by 1, but use 0 internally
// sanity checks
if len(data.entries) == 0 {
return
}
if col >= len(data.headers) {
// fall back to default column
col = 0
}
// actual sorting
sort.SliceStable(data.entries, func(i, j int) bool {
return compare(data.entries[i][col], data.entries[j][col])
})
}
func compare(a string, b string) bool {
var comp bool
switch SortMode {
case "numeric":
left, err := strconv.Atoi(a)
if err != nil {
left = 0
}
right, err := strconv.Atoi(b)
if err != nil {
right = 0
}
comp = left < right
case "duration":
left, err := str2duration.ParseDuration(a)
if err != nil {
left = 0
}
right, err := str2duration.ParseDuration(b)
if err != nil {
right = 0
}
comp = left.Seconds() < right.Seconds()
case "time":
left, _ := dateparse.ParseAny(a)
right, _ := dateparse.ParseAny(b)
comp = left.Unix() < right.Unix()
default:
comp = a < b
}
if SortDescending {
comp = !comp
}
return comp
}

26
main.go
View File

@@ -1,26 +0,0 @@
/*
Copyright © 2022 Thomas von Dein
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/tlinden/tablizer/cmd"
)
func main() {
cmd.Execute()
}

View File

@@ -1,65 +0,0 @@
#!/bin/bash
# Copyright © 2022 Thomas von Dein
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# get list with: go tool dist list
DIST="darwin/amd64
freebsd/amd64
linux/amd64
netbsd/amd64
openbsd/amd64
windows/amd64"
tool="$1"
version="$2"
if test -z "$version"; then
echo "Usage: $0 <tool name> <release version>"
exit 1
fi
rm -rf releases
mkdir -p releases
for D in $DIST; do
os=${D/\/*/}
arch=${D/*\//}
binfile="releases/${tool}-${os}-${arch}-${version}"
tardir="${tool}-${os}-${arch}-${version}"
tarfile="releases/${tool}-${os}-${arch}-${version}.tar.gz"
set -x
GOOS=${os} GOARCH=${arch} go build -o ${binfile} -ldflags "-X 'github.com/tlinden/tablizer/lib.VERSION=${version}'"
mkdir -p ${tardir}
cp ${binfile} README.md LICENSE ${tardir}/
echo 'tool = tablizer
PREFIX = /usr/local
UID = root
GID = 0
install:
install -d -o $(UID) -g $(GID) $(PREFIX)/bin
install -d -o $(UID) -g $(GID) $(PREFIX)/man/man1
install -o $(UID) -g $(GID) -m 555 $(tool) $(PREFIX)/sbin/
install -o $(UID) -g $(GID) -m 444 $(tool).1 $(PREFIX)/man/man1/' > ${tardir}/Makefile
tar cpzf ${tarfile} ${tardir}
sha256sum ${binfile} | cut -d' ' -f1 > ${binfile}.sha256
sha256sum ${tarfile} | cut -d' ' -f1 > ${tarfile}.sha256
rm -rf ${tardir}
set +x
done

View File

@@ -1,369 +0,0 @@
.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.42)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is >0, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{\
. if \nF \{\
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{\
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\"
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
.\" Fear. Run. Save yourself. No user-serviceable parts.
. \" fudge factors for nroff and troff
.if n \{\
. ds #H 0
. ds #V .8m
. ds #F .3m
. ds #[ \f1
. ds #] \fP
.\}
.if t \{\
. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
. ds #V .6m
. ds #F 0
. ds #[ \&
. ds #] \&
.\}
. \" simple accents for nroff and troff
.if n \{\
. ds ' \&
. ds ` \&
. ds ^ \&
. ds , \&
. ds ~ ~
. ds /
.\}
.if t \{\
. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
.\}
. \" troff and (daisy-wheel) nroff accents
.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
.ds ae a\h'-(\w'a'u*4/10)'e
.ds Ae A\h'-(\w'A'u*4/10)'E
. \" corrections for vroff
.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
. \" for low resolution devices (crt and lpr)
.if \n(.H>23 .if \n(.V>19 \
\{\
. ds : e
. ds 8 ss
. ds o a
. ds d- d\h'-1'\(ga
. ds D- D\h'-1'\(hy
. ds th \o'bp'
. ds Th \o'LP'
. ds ae ae
. ds Ae AE
.\}
.rm #[ #] #H #V #F C
.\" ========================================================================
.\"
.IX Title "TABLIZER 1"
.TH TABLIZER 1 "2022-10-15" "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
.nh
.SH "NAME"
tablizer \- Manipulate tabular output of other programs
.SH "SYNOPSIS"
.IX Header "SYNOPSIS"
.Vb 2
\& Usage:
\& tablizer [regex] [file, ...] [flags]
\&
\& Flags:
\& \-c, \-\-columns string Only show the speficied columns (separated by ,)
\& \-d, \-\-debug Enable debugging
\& \-h, \-\-help help for tablizer
\& \-v, \-\-invert\-match select non\-matching rows
\& \-m, \-\-man Display manual page
\& \-n, \-\-no\-numbering Disable header numbering
\& \-N, \-\-no\-color Disable pattern highlighting
\& \-o, \-\-output string Output mode \- one of: orgtbl, markdown, extended, ascii(default)
\& \-X, \-\-extended Enable extended output
\& \-M, \-\-markdown Enable markdown table output
\& \-O, \-\-orgtbl Enable org\-mode table output
\& \-s, \-\-separator string Custom field separator
\& \-a, \-\-sort\-age sort according to age (duration) string
\& \-k, \-\-sort\-by int Sort by column (default: 1)
\& \-D, \-\-sort\-desc Sort in descending order (default: ascending)
\& \-i, \-\-sort\-numeric sort according to string numerical value
\& \-t, \-\-sort\-time sort according to time string
\& \-v, \-\-version Print program version
.Ve
.SH "DESCRIPTION"
.IX Header "DESCRIPTION"
Many programs generate tabular output. But sometimes you need to
post-process these tables, you may need to remove one or more columns
or you may want to filter for some pattern (See \s-1PATTERNS\s0) or you
may need the output in another program and need to parse it somehow.
Standard unix tools such as \fBawk\fR\|(1), \fBgrep\fR\|(1) or \fBcolumn\fR\|(1) may help, but
sometimes it's a tedious business.
.PP
Let's take the output of the tool kubectl. It contains cells with
withespace and they do not separate columns by \s-1TAB\s0 characters. This is
not easy to process.
.PP
You can use \fBtablizer\fR to do these and more things.
.PP
\&\fBtablizer\fR analyses the header fields of a table, registers the
column positions of each header field and separates columns by those
positions.
.PP
Without any options it reads its input from \f(CW\*(C`STDIN\*(C'\fR, but you can also
specify a file as a parameter. If you want to reduce the output by
some regular expression, just specify it as its first parameter. You
may also use the \fB\-v\fR option to exclude all rows which match the
pattern. Hence:
.PP
.Vb 2
\& # read from STDIN
\& kubectl get pods | tablizer
\&
\& # read a file
\& tablizer filename
\&
\& # search for pattern in a file (works like grep)
\& tablizer regex 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.:
.PP
.Vb 1
\& NAME(1) READY(2) STATUS(3) RESTARTS(4) AGE(5)
.Ve
.PP
These numbers denote the column and you can use them to specify which
columns you want to have in your output (see \s-1COLUMNS\s0:
.PP
.Vb 1
\& kubectl get pods | tablizer \-c1,3
.Ve
.PP
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.
.PP
By default, if a \fBpattern\fR has been speficied, matches will be
highlighted. You can disable this behavior with the \fB\-N\fR option.
.PP
Use the \fB\-k\fR option to specify by which column to sort the tabular
data (as in \s-1GNU\s0 \fBsort\fR\|(1)). The default sort column is the first one. To
disable sorting at all, supply 0 (Zero) to \-k. The default sort order
is ascending. You can change this to descending order using the option
\&\fB\-D\fR. The default sort order is by string, but there are other sort
modes:
.IP "\fB\-a \-\-sort\-age\fR" 4
.IX Item "-a --sort-age"
Sorts duration strings like \*(L"1d4h32m51s\*(R".
.IP "\fB\-i \-\-sort\-numeric\fR" 4
.IX Item "-i --sort-numeric"
Sorts numeric fields.
.IP "\fB\-t \-\-sort\-time\fR" 4
.IX Item "-t --sort-time"
Sorts timestamps.
.PP
Finally the \fB\-d\fR option enables debugging output which is mostly
useful for the developer.
.SS "\s-1PATTERNS\s0"
.IX Subsection "PATTERNS"
You can reduce the rows being displayed by using a regular expression
pattern. The regexp is \s-1PCRE\s0 compatible, refer to the syntax cheat
sheet here: <https://github.com/google/re2/wiki/Syntax>. If you want
to read a more comprehensive documentation about the topic and have
perl installed you can read it with:
.PP
.Vb 1
\& perldoc perlre
.Ve
.PP
Or read it online: <https://perldoc.perl.org/perlre>.
.PP
A note on modifiers: the regexp engine used in tablizer uses another
modifier syntax:
.PP
.Vb 1
\& (?MODIFIER)
.Ve
.PP
The most important modifiers are:
.PP
\&\f(CW\*(C`i\*(C'\fR ignore case
\&\f(CW\*(C`m\*(C'\fR multiline mode
\&\f(CW\*(C`s\*(C'\fR single line mode
.PP
Example for a case insensitive search:
.PP
.Vb 1
\& kubectl get pods \-A | tablizer "(?i)account"
.Ve
.SS "\s-1COLUMNS\s0"
.IX Subsection "COLUMNS"
The parameter \fB\-c\fR can be used to specify, which columns to
display. By default tablizer numerizes the header names and these
numbers can be used to specify which header to display, see example
above.
.PP
However, beside numbers, you can also use regular expressions with
\&\fB\-c\fR, also separated by comma. And you can mix column numbers with
regexps.
.PP
Lets take this table:
.PP
.Vb 4
\& PID TTY TIME CMD
\& 14001 pts/0 00:00:00 bash
\& 42871 pts/0 00:00:00 ps
\& 42872 pts/0 00:00:00 sed
.Ve
.PP
We want to see only the \s-1CMD\s0 column and use a regex for this:
.PP
.Vb 6
\& ps | tablizer \-s \*(Aq\es+\*(Aq \-c C
\& CMD(4)
\& bash
\& ps
\& tablizer
\& sed
.Ve
.PP
where \*(L"C\*(R" is our regexp which matches \s-1CMD.\s0
.SS "\s-1OUTPUT MODES\s0"
.IX Subsection "OUTPUT MODES"
There might be cases when the tabular output of a program is way too
large for your current terminal but you still need to see every
column. In such cases the \fB\-o extended\fR or \fB\-X\fR option can be
useful which enables \fIextended mode\fR. In this mode, each row will be
printed vertically, header left, value right, aligned by the field
widths. Here's an example:
.PP
.Vb 6
\& kubectl get pods | ./tablizer \-o extended
\& NAME: repldepl\-7bcd8d5b64\-7zq4l
\& READY: 1/1
\& STATUS: Running
\& RESTARTS: 1 (71m ago)
\& AGE: 5h28m
.Ve
.PP
You can of course still use a regex to reduce the number of rows
displayed.
.PP
The option \fB\-o shell\fR can be used if the output has to be processed
by the shell, it prints variable assignments for each cell, one line
per row:
.PP
.Vb 4
\& kubectl get pods | ./tablizer \-o extended ./tablizer \-o shell
\& NAME="repldepl\-7bcd8d5b64\-7zq4l" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h"
\& NAME="repldepl\-7bcd8d5b64\-m48n8" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h"
\& NAME="repldepl\-7bcd8d5b64\-q2bf4" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h"
.Ve
.PP
You can use this in an eval loop.
.PP
Beside normal ascii mode (the default) and extended mode there are
more output modes available: \fBorgtbl\fR which prints an Emacs org-mode
table and \fBmarkdown\fR which prints a Markdown table.
.SH "BUGS"
.IX Header "BUGS"
In order to report a bug, unexpected behavior, feature requests
or to submit a patch, please open an issue on github:
<https://github.com/TLINDEN/tablizer/issues>.
.SH "LICENSE"
.IX Header "LICENSE"
This software is licensed under the \s-1GNU GENERAL PUBLIC LICENSE\s0 version 3.
.PP
Copyright (c) 2022 by Thomas von Dein
.PP
This software uses the following \s-1GO\s0 libraries:
.IP "repr (https://github.com/alecthomas/repr)" 4
.IX Item "repr (https://github.com/alecthomas/repr)"
Released under the \s-1MIT\s0 License, Copyright (c) 2016 Alec Thomas
.IP "cobra (https://github.com/spf13/cobra)" 4
.IX Item "cobra (https://github.com/spf13/cobra)"
Released under the Apache 2.0 license, Copyright 2013\-2022 The Cobra Authors
.SH "AUTHORS"
.IX Header "AUTHORS"
Thomas von Dein \fBtom \s-1AT\s0 vondein \s-1DOT\s0 org\fR

View File

@@ -1,234 +0,0 @@
=head1 NAME
tablizer - Manipulate tabular output of other programs
=head1 SYNOPSIS
Usage:
tablizer [regex] [file, ...] [flags]
Flags:
-c, --columns string Only show the speficied columns (separated by ,)
-d, --debug Enable debugging
-h, --help help for tablizer
-v, --invert-match select non-matching rows
-m, --man Display manual page
-n, --no-numbering Disable header numbering
-N, --no-color Disable pattern highlighting
-o, --output string Output mode - one of: orgtbl, markdown, extended, ascii(default)
-X, --extended Enable extended output
-M, --markdown Enable markdown table output
-O, --orgtbl Enable org-mode table output
-s, --separator string Custom field separator
-a, --sort-age sort according to age (duration) string
-k, --sort-by int Sort by column (default: 1)
-D, --sort-desc Sort in descending order (default: ascending)
-i, --sort-numeric sort according to string numerical value
-t, --sort-time sort according to time string
-v, --version Print program version
=head1 DESCRIPTION
Many programs generate tabular output. But sometimes you need to
post-process these tables, you may need to remove one or more columns
or you may want to filter for some pattern (See L<PATTERNS>) or you
may need the output in another program and need to parse it somehow.
Standard unix tools such as awk(1), grep(1) or column(1) may help, but
sometimes it's a tedious business.
Let's take the output of the tool kubectl. It contains cells with
withespace and they do not separate columns by TAB characters. This is
not easy to process.
You can use B<tablizer> to do these and more things.
B<tablizer> analyses the header fields of a table, registers the
column positions of each header field and separates columns by those
positions.
Without any options it reads its input from C<STDIN>, but you can also
specify a file as a parameter. If you want to reduce the output by
some regular expression, just specify it as its first parameter. You
may also use the B<-v> option to exclude all rows which match the
pattern. Hence:
# read from STDIN
kubectl get pods | tablizer
# read a file
tablizer filename
# search for pattern in a file (works like grep)
tablizer regex 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.:
NAME(1) READY(2) STATUS(3) RESTARTS(4) AGE(5)
These numbers denote the column and you can use them to specify which
columns you want to have in your output (see L<COLUMNS>:
kubectl get pods | tablizer -c1,3
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.
By default, if a B<pattern> has been speficied, matches will be
highlighted. You can disable this behavior with the B<-N> option.
Use the B<-k> option to specify by which column to sort the tabular
data (as in GNU sort(1)). The default sort column is the first one. To
disable sorting at all, supply 0 (Zero) to -k. The default sort order
is ascending. You can change this to descending order using the option
B<-D>. The default sort order is by string, but there are other sort
modes:
=over
=item B<-a --sort-age>
Sorts duration strings like "1d4h32m51s".
=item B<-i --sort-numeric>
Sorts numeric fields.
=item B<-t --sort-time>
Sorts timestamps.
=back
Finally the B<-d> option enables debugging output which is mostly
useful for the developer.
=head2 PATTERNS
You can reduce the rows being displayed by using a regular expression
pattern. The regexp is PCRE compatible, refer to the syntax cheat
sheet here: L<https://github.com/google/re2/wiki/Syntax>. If you want
to read a more comprehensive documentation about the topic and have
perl installed you can read it with:
perldoc perlre
Or read it online: L<https://perldoc.perl.org/perlre>.
A note on modifiers: the regexp engine used in tablizer uses another
modifier syntax:
(?MODIFIER)
The most important modifiers are:
C<i> ignore case
C<m> multiline mode
C<s> single line mode
Example for a case insensitive search:
kubectl get pods -A | tablizer "(?i)account"
=head2 COLUMNS
The parameter B<-c> can be used to specify, which columns to
display. By default tablizer numerizes the header names and these
numbers can be used to specify which header to display, see example
above.
However, beside numbers, you can also use regular expressions with
B<-c>, also separated by comma. And you can mix column numbers with
regexps.
Lets take this table:
PID TTY TIME CMD
14001 pts/0 00:00:00 bash
42871 pts/0 00:00:00 ps
42872 pts/0 00:00:00 sed
We want to see only the CMD column and use a regex for this:
ps | tablizer -s '\s+' -c C
CMD(4)
bash
ps
tablizer
sed
where "C" is our regexp which matches CMD.
=head2 OUTPUT MODES
There might be cases when the tabular output of a program is way too
large for your current terminal but you still need to see every
column. In such cases the B<-o extended> or B<-X> option can be
useful which enables I<extended mode>. In this mode, each row will be
printed vertically, header left, value right, aligned by the field
widths. Here's an example:
kubectl get pods | ./tablizer -o extended
NAME: repldepl-7bcd8d5b64-7zq4l
READY: 1/1
STATUS: Running
RESTARTS: 1 (71m ago)
AGE: 5h28m
You can of course still use a regex to reduce the number of rows
displayed.
The option B<-o shell> can be used if the output has to be processed
by the shell, it prints variable assignments for each cell, one line
per row:
kubectl get pods | ./tablizer -o extended ./tablizer -o shell
NAME="repldepl-7bcd8d5b64-7zq4l" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h"
NAME="repldepl-7bcd8d5b64-m48n8" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h"
NAME="repldepl-7bcd8d5b64-q2bf4" READY="1/1" STATUS="Running" RESTARTS="9 (47m ago)" AGE="4d23h"
You can use this in an eval loop.
Beside normal ascii mode (the default) and extended mode there are
more output modes available: B<orgtbl> which prints an Emacs org-mode
table and B<markdown> which prints a Markdown table.
=head1 BUGS
In order to report a bug, unexpected behavior, feature requests
or to submit a patch, please open an issue on github:
L<https://github.com/TLINDEN/tablizer/issues>.
=head1 LICENSE
This software is licensed under the GNU GENERAL PUBLIC LICENSE version 3.
Copyright (c) 2022 by Thomas von Dein
This software uses the following GO libraries:
=over 4
=item repr (https://github.com/alecthomas/repr)
Released under the MIT License, Copyright (c) 2016 Alec Thomas
=item cobra (https://github.com/spf13/cobra)
Released under the Apache 2.0 license, Copyright 2013-2022 The Cobra Authors
=back
=head1 AUTHORS
Thomas von Dein B<tom AT vondein DOT org>
=cut