diff --git a/cmd/root.go b/cmd/root.go
index 32ce390..24815af 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -90,6 +90,7 @@ func init() {
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 ,)")
+ rootCmd.PersistentFlags().IntVarP(&lib.SortByColumn, "sort-by", "k", 0, "Sort by column (default: 1)")
// output flags, only 1 allowed, hidden, since just short cuts
rootCmd.PersistentFlags().BoolVarP(&lib.OutflagExtended, "extended", "X", false, "Enable extended output")
diff --git a/cmd/tablizer.go b/cmd/tablizer.go
index 69c11d8..54328eb 100644
--- a/cmd/tablizer.go
+++ b/cmd/tablizer.go
@@ -20,6 +20,7 @@ SYNOPSIS
-M, --markdown Enable markdown table output
-O, --orgtbl Enable org-mode table output
-s, --separator string Custom field separator
+ -k, --sort-by int Sort by column (default: 1)
-v, --version Print program version
DESCRIPTION
@@ -74,6 +75,10 @@ DESCRIPTION
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.
+
Finally the -d option enables debugging output which is mostly usefull
for the developer.
diff --git a/lib/common.go b/lib/common.go
index e7fcdec..1279e7c 100644
--- a/lib/common.go
+++ b/lib/common.go
@@ -68,10 +68,22 @@ var (
validOutputmodes = "(orgtbl|markdown|extended|ascii)"
// main program version
- Version = "v1.0.7"
+ Version = "v1.0.8"
// 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
)
+
+// 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
+}
diff --git a/lib/parser.go b/lib/parser.go
index 981ceda..04894f9 100644
--- a/lib/parser.go
+++ b/lib/parser.go
@@ -27,15 +27,6 @@ import (
"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.
*/
diff --git a/lib/printer.go b/lib/printer.go
index 6f3f76e..6eb8178 100644
--- a/lib/printer.go
+++ b/lib/printer.go
@@ -26,11 +26,19 @@ import (
)
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)
diff --git a/lib/printer_test.go b/lib/printer_test.go
index a0b132f..a615e50 100644
--- a/lib/printer_test.go
+++ b/lib/printer_test.go
@@ -25,6 +25,20 @@ import (
"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,
@@ -51,18 +65,22 @@ func TestPrinter(t *testing.T) {
"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
@@ -73,16 +91,9 @@ THREE(3): X`,
}
NoColor = true
+ SortByColumn = 0 // disable sorting
- r, w, err := os.Pipe()
- 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)
+ origStdout, reader := stdout2pipe(t)
for mode, expect := range expects {
testname := fmt.Sprintf("print-%s", mode)
@@ -93,7 +104,7 @@ THREE(3): X`,
printData(&data)
buf := make([]byte, 1024)
- n, err := r.Read(buf)
+ n, err := reader.Read(buf)
if err != nil {
t.Fatal(err)
}
@@ -101,7 +112,8 @@ THREE(3): X`,
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))
+ 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
}
+
+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
+}
diff --git a/lib/sort.go b/lib/sort.go
new file mode 100644
index 0000000..25b8b57
--- /dev/null
+++ b/lib/sort.go
@@ -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 .
+*/
+
+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]
+ })
+}
diff --git a/tablizer.1 b/tablizer.1
index 05bf4d6..766dabd 100644
--- a/tablizer.1
+++ b/tablizer.1
@@ -133,7 +133,7 @@
.\" ========================================================================
.\"
.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
.\" way too many mistakes in technical documents.
.if n .ad l
@@ -159,6 +159,7 @@ tablizer \- Manipulate tabular output of other programs
\& \-M, \-\-markdown Enable markdown table output
\& \-O, \-\-orgtbl Enable org\-mode table output
\& \-s, \-\-separator string Custom field separator
+\& \-k, \-\-sort\-by int Sort by column (default: 1)
\& \-v, \-\-version Print program version
.Ve
.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
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.
+.PP
Finally the \fB\-d\fR option enables debugging output which is mostly
usefull for the developer.
.SS "\s-1PATTERNS\s0"
diff --git a/tablizer.pod b/tablizer.pod
index 266b667..59a88e8 100644
--- a/tablizer.pod
+++ b/tablizer.pod
@@ -20,6 +20,7 @@ tablizer - Manipulate tabular output of other programs
-M, --markdown Enable markdown table output
-O, --orgtbl Enable org-mode table output
-s, --separator string Custom field separator
+ -k, --sort-by int Sort by column (default: 1)
-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 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.
+
Finally the B<-d> option enables debugging output which is mostly
usefull for the developer.