Compare commits

...

3 Commits

12 changed files with 221 additions and 41 deletions

View File

@@ -45,6 +45,7 @@ buildlocal:
release: release:
./mkrel.sh $(tool) $(version) ./mkrel.sh $(tool) $(version)
gh release create $(version) --generate-notes releases/*
install: buildlocal install: buildlocal
install -d -o $(UID) -g $(GID) $(PREFIX)/bin install -d -o $(UID) -g $(GID) $(PREFIX)/bin

View File

@@ -20,13 +20,13 @@ But you're only interested in the NAME and STATUS columns. Here's how
to do this with tablizer: to do this with tablizer:
``` ```
% kubectl get pods | ./tablizer % kubectl get pods | tablizer
NAME(1) READY(2) STATUS(3) RESTARTS(4) AGE(5) NAME(1) READY(2) STATUS(3) RESTARTS(4) AGE(5)
repldepl-7bcd8d5b64-7zq4l 1/1 Running 1 (69m ago) 5h26m repldepl-7bcd8d5b64-7zq4l 1/1 Running 1 (69m ago) 5h26m
repldepl-7bcd8d5b64-m48n8 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 repldepl-7bcd8d5b64-q2bf4 1/1 Running 1 (69m ago) 5h26m
% kubectl get pods | ./tablizer -c 1,3 % kubectl get pods | tablizer -c 1,3
NAME(1) STATUS(3) NAME(1) STATUS(3)
repldepl-7bcd8d5b64-7zq4l Running repldepl-7bcd8d5b64-7zq4l Running
repldepl-7bcd8d5b64-m48n8 Running repldepl-7bcd8d5b64-m48n8 Running
@@ -35,10 +35,11 @@ repldepl-7bcd8d5b64-q2bf4 Running
Another use case is when the tabular output is so wide that lines are Another use case is when the tabular output is so wide that lines are
being broken and the whole output is completely distorted. In such a being broken and the whole output is completely distorted. In such a
case you can use the `-x` flag to get an output similar to `\x` in `psql`: case you can use the `-o extended | -X` flag to get an output similar
to `\x` in `psql`:
``` ```
% kubectl get pods | ./tablizer -x % kubectl get pods | tablizer -X
NAME: repldepl-7bcd8d5b64-7zq4l NAME: repldepl-7bcd8d5b64-7zq4l
READY: 1/1 READY: 1/1
STATUS: Running STATUS: Running
@@ -63,11 +64,12 @@ Tablize can read one or more files or - if none specified - from STDIN.
You can also specify a regex pattern to reduce the output: You can also specify a regex pattern to reduce the output:
``` ```
% kubectl get pods | ./tablizer q2bf4 % kubectl get pods | tablizer q2bf4
NAME(1) READY(2) STATUS(3) RESTARTS(4) AGE(5) NAME(1) READY(2) STATUS(3) RESTARTS(4) AGE(5)
repldepl-7bcd8d5b64-q2bf4 1/1 Running 1 (69m ago) 5h26m repldepl-7bcd8d5b64-q2bf4 1/1 Running 1 (69m ago) 5h26m
``` ```
There are more output modes like org-mode (orgtbl) and markdown.
## Installation ## Installation
@@ -106,6 +108,10 @@ https://github.com/TLINDEN/tablizer/blob/main/tablizer.pod
Or if you cloned the repository you can read it this way (perl needs Or if you cloned the repository you can read it this way (perl needs
to be installed though): `perldoc tablizer.pod`. to be installed though): `perldoc tablizer.pod`.
If you have the binary installed, you can also read the man page with
this command:
tablizer --man
## Getting help ## Getting help

View File

@@ -90,6 +90,7 @@ func init() {
rootCmd.PersistentFlags().BoolVarP(&ShowManual, "man", "m", false, "Display manual page") 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.Separator, "separator", "s", lib.DefaultSeparator, "Custom field separator")
rootCmd.PersistentFlags().StringVarP(&lib.Columns, "columns", "c", "", "Only show the speficied columns (separated by ,)") rootCmd.PersistentFlags().StringVarP(&lib.Columns, "columns", "c", "", "Only show the speficied columns (separated by ,)")
rootCmd.PersistentFlags().IntVarP(&lib.SortByColumn, "sort-by", "k", 0, "Sort by column (default: 1)")
// output flags, only 1 allowed, hidden, since just short cuts // 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.OutflagExtended, "extended", "X", false, "Enable extended output")

View File

@@ -20,6 +20,7 @@ SYNOPSIS
-M, --markdown Enable markdown table output -M, --markdown Enable markdown table output
-O, --orgtbl Enable org-mode table output -O, --orgtbl Enable org-mode table output
-s, --separator string Custom field separator -s, --separator string Custom field separator
-k, --sort-by int Sort by column (default: 1)
-v, --version Print program version -v, --version Print program version
DESCRIPTION DESCRIPTION
@@ -74,6 +75,10 @@ DESCRIPTION
By default, if a pattern has been speficied, matches will be By default, if a pattern has been speficied, matches will be
highlighted. You can disable this behavior with the -N option. 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.
Finally the -d option enables debugging output which is mostly usefull Finally the -d option enables debugging output which is mostly usefull
for the developer. for the developer.

View File

@@ -68,10 +68,22 @@ var (
validOutputmodes = "(orgtbl|markdown|extended|ascii)" validOutputmodes = "(orgtbl|markdown|extended|ascii)"
// main program version // main program version
Version = "v1.0.7" Version = "v1.0.8"
// generated version string, used by -v contains lib.Version on // generated version string, used by -v contains lib.Version on
// main branch, and lib.Version-$branch-$lastcommit-$date on // main branch, and lib.Version-$branch-$lastcommit-$date on
// development branch // development branch
VERSION string VERSION string
// sorting
SortByColumn int
) )
// 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

@@ -27,15 +27,6 @@ import (
"strings" "strings"
) )
// 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
}
/* /*
Parse tabular input. Parse tabular input.
*/ */

View File

@@ -26,11 +26,19 @@ import (
) )
func printData(data *Tabdata) { func printData(data *Tabdata) {
// some output preparations:
if OutputMode != "shell" { if OutputMode != "shell" {
// not needed in eval string
numberizeHeaders(data) numberizeHeaders(data)
} }
// remove unwanted columns, if any
reduceColumns(data) reduceColumns(data)
// sort the data
sortTable(data, SortByColumn)
switch OutputMode { switch OutputMode {
case "extended": case "extended":
printExtendedData(data) printExtendedData(data)

View File

@@ -25,6 +25,20 @@ import (
"testing" "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) { func TestPrinter(t *testing.T) {
startdata := Tabdata{ startdata := Tabdata{
maxwidthHeader: 5, maxwidthHeader: 5,
@@ -51,18 +65,22 @@ func TestPrinter(t *testing.T) {
"ascii": `ONE(1) TWO(2) THREE(3) "ascii": `ONE(1) TWO(2) THREE(3)
asd igig cxxxncnc asd igig cxxxncnc
19191 EDD 1 X`, 19191 EDD 1 X`,
"orgtbl": `|--------+--------+----------| "orgtbl": `|--------+--------+----------|
| ONE(1) | TWO(2) | THREE(3) | | ONE(1) | TWO(2) | THREE(3) |
|--------+--------+----------| |--------+--------+----------|
| asd | igig | cxxxncnc | | asd | igig | cxxxncnc |
| 19191 | EDD 1 | X | | 19191 | EDD 1 | X |
|--------+--------+----------|`, |--------+--------+----------|`,
"markdown": `| ONE(1) | TWO(2) | THREE(3) | "markdown": `| ONE(1) | TWO(2) | THREE(3) |
|--------|--------|----------| |--------|--------|----------|
| asd | igig | cxxxncnc | | asd | igig | cxxxncnc |
| 19191 | EDD 1 | X |`, | 19191 | EDD 1 | X |`,
"shell": `ONE="asd" TWO="igig" THREE="cxxxncnc" "shell": `ONE="asd" TWO="igig" THREE="cxxxncnc"
ONE="19191" TWO="EDD 1" THREE="X"`, ONE="19191" TWO="EDD 1" THREE="X"`,
"extended": `ONE(1): asd "extended": `ONE(1): asd
TWO(2): igig TWO(2): igig
THREE(3): cxxxncnc THREE(3): cxxxncnc
@@ -73,16 +91,9 @@ THREE(3): X`,
} }
NoColor = true NoColor = true
SortByColumn = 0 // disable sorting
r, w, err := os.Pipe() origStdout, reader := stdout2pipe(t)
if err != nil {
t.Fatal(err)
}
origStdout := os.Stdout
os.Stdout = w
// we need to tell the color mode the io.Writer, even if we don't usw colorization
color.SetOutput(w)
for mode, expect := range expects { for mode, expect := range expects {
testname := fmt.Sprintf("print-%s", mode) testname := fmt.Sprintf("print-%s", mode)
@@ -93,7 +104,7 @@ THREE(3): X`,
printData(&data) printData(&data)
buf := make([]byte, 1024) buf := make([]byte, 1024)
n, err := r.Read(buf) n, err := reader.Read(buf)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -101,7 +112,8 @@ THREE(3): X`,
output := strings.TrimSpace(string(buf)) output := strings.TrimSpace(string(buf))
if output != expect { if output != expect {
t.Errorf("output mode: %s, got:\n%s\nwant:\n%s\n (%d <=> %d)", mode, output, expect, len(output), len(expect)) t.Errorf("output mode: %s, got:\n%s\nwant:\n%s\n (%d <=> %d)",
mode, output, expect, len(output), len(expect))
} }
}) })
} }
@@ -110,3 +122,91 @@ THREE(3): X`,
os.Stdout = origStdout 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{
[]string{
"abc", "345", "b1",
},
[]string{
"bcd", "234", "a2",
},
[]string{
"cde", "123", "c3",
},
},
}
var tests = []struct {
data Tabdata
sortby int
expect string
}{
{
data: startdata,
sortby: 1,
expect: `ONE(1) TWO(2) THREE(3)
abc 345 b1
bcd 234 a2
cde 123 c3`,
},
{
data: startdata,
sortby: 2,
expect: `ONE(1) TWO(2) THREE(3)
cde 123 c3
bcd 234 a2
abc 345 b1`,
},
{
data: startdata,
sortby: 3,
expect: `ONE(1) TWO(2) THREE(3)
bcd 234 a2
abc 345 b1
cde 123 c3`,
},
}
NoColor = true
OutputMode = "ascii"
origStdout, reader := stdout2pipe(t)
for _, tt := range tests {
testname := fmt.Sprintf("print-sorted-table-%d", tt.sortby)
t.Run(testname, func(t *testing.T) {
SortByColumn = tt.sortby
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
}

46
lib/sort.go Normal file
View File

@@ -0,0 +1,46 @@
/*
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 (
"sort"
)
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 data.entries[i][col] < data.entries[j][col]
})
}

View File

@@ -43,7 +43,7 @@ for D in $DIST; do
tardir="${tool}-${os}-${arch}-${version}" tardir="${tool}-${os}-${arch}-${version}"
tarfile="releases/${tool}-${os}-${arch}-${version}.tar.gz" tarfile="releases/${tool}-${os}-${arch}-${version}.tar.gz"
set -x set -x
GOOS=${os} GOARCH=${arch} go build -o ${binfile} GOOS=${os} GOARCH=${arch} go build -o ${binfile} -ldflags "-X 'github.com/tlinden/tablizer/lib.VERSION=${version}'"
mkdir -p ${tardir} mkdir -p ${tardir}
cp ${binfile} README.md LICENSE ${tardir}/ cp ${binfile} README.md LICENSE ${tardir}/
echo 'tool = tablizer echo 'tool = tablizer

View File

@@ -133,7 +133,7 @@
.\" ======================================================================== .\" ========================================================================
.\" .\"
.IX Title "TABLIZER 1" .IX Title "TABLIZER 1"
.TH TABLIZER 1 "2022-10-10" "1" "User Commands" .TH TABLIZER 1 "2022-10-13" "1" "User Commands"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents. .\" way too many mistakes in technical documents.
.if n .ad l .if n .ad l
@@ -159,6 +159,7 @@ tablizer \- Manipulate tabular output of other programs
\& \-M, \-\-markdown Enable markdown table output \& \-M, \-\-markdown Enable markdown table output
\& \-O, \-\-orgtbl Enable org\-mode table output \& \-O, \-\-orgtbl Enable org\-mode table output
\& \-s, \-\-separator string Custom field separator \& \-s, \-\-separator string Custom field separator
\& \-k, \-\-sort\-by int Sort by column (default: 1)
\& \-v, \-\-version Print program version \& \-v, \-\-version Print program version
.Ve .Ve
.SH "DESCRIPTION" .SH "DESCRIPTION"
@@ -222,6 +223,10 @@ The numbering can be suppressed by using the \fB\-n\fR option.
By default, if a \fBpattern\fR has been speficied, matches will be By default, if a \fBpattern\fR has been speficied, matches will be
highlighted. You can disable this behavior with the \fB\-N\fR option. highlighted. You can disable this behavior with the \fB\-N\fR option.
.PP .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.
.PP
Finally the \fB\-d\fR option enables debugging output which is mostly Finally the \fB\-d\fR option enables debugging output which is mostly
usefull for the developer. usefull for the developer.
.SS "\s-1PATTERNS\s0" .SS "\s-1PATTERNS\s0"

View File

@@ -20,6 +20,7 @@ tablizer - Manipulate tabular output of other programs
-M, --markdown Enable markdown table output -M, --markdown Enable markdown table output
-O, --orgtbl Enable org-mode table output -O, --orgtbl Enable org-mode table output
-s, --separator string Custom field separator -s, --separator string Custom field separator
-k, --sort-by int Sort by column (default: 1)
-v, --version Print program version -v, --version Print program version
@@ -78,6 +79,10 @@ The numbering can be suppressed by using the B<-n> option.
By default, if a B<pattern> has been speficied, matches will be By default, if a B<pattern> has been speficied, matches will be
highlighted. You can disable this behavior with the B<-N> option. 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.
Finally the B<-d> option enables debugging output which is mostly Finally the B<-d> option enables debugging output which is mostly
usefull for the developer. usefull for the developer.