Merge pull request #2 from TLINDEN/development

Merge latest dev
This commit is contained in:
T.v.Dein
2022-10-03 13:35:25 +02:00
committed by GitHub
10 changed files with 98 additions and 51 deletions

4
TODO
View File

@@ -1,5 +1 @@
Add a mode like FreeBSD stat(1):
stat -s dead.letter
st_dev=170671546954750497 st_ino=159667 st_mode=0100644 st_nlink=1 st_uid=1001 st_gid=1001 st_rdev=18446744073709551615 st_size=573 st_atime=1661994007 st_mtime=1661961878 st_ctime=1661961878 st_birthtime=1658394900 st_blksize=4096 st_blocks=3 st_flags=2048

View File

@@ -65,11 +65,13 @@ func init() {
rootCmd.PersistentFlags().BoolVarP(&lib.OutflagExtended, "extended", "X", false, "Enable extended output") 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.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.OutflagOrgtable, "orgtbl", "O", false, "Enable org-mode table output")
rootCmd.MarkFlagsMutuallyExclusive("extended", "markdown", "orgtbl") 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("extended")
rootCmd.Flags().MarkHidden("orgtbl") rootCmd.Flags().MarkHidden("orgtbl")
rootCmd.Flags().MarkHidden("markdown") rootCmd.Flags().MarkHidden("markdown")
rootCmd.Flags().MarkHidden("shell")
// same thing but more common, takes precedence over above group // same thing but more common, takes precedence over above group
rootCmd.PersistentFlags().StringVarP(&lib.OutputMode, "output", "o", "", "Output mode - one of: orgtbl, markdown, extended, ascii(default)") rootCmd.PersistentFlags().StringVarP(&lib.OutputMode, "output", "o", "", "Output mode - one of: orgtbl, markdown, extended, shell, ascii(default)")
} }

View File

@@ -28,7 +28,8 @@ var Separator string
var OutflagExtended bool var OutflagExtended bool
var OutflagMarkdown bool var OutflagMarkdown bool
var OutflagOrgtable bool var OutflagOrgtable bool
var OutflagShell bool
var OutputMode string var OutputMode string
var Version = "v1.0.2" var Version = "v1.0.3"
var validOutputmodes = "(orgtbl|markdown|extended|ascii)" var validOutputmodes = "(orgtbl|markdown|extended|ascii)"

View File

@@ -20,17 +20,11 @@ package lib
import ( import (
"errors" "errors"
"fmt" "fmt"
"os"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
) )
func die(v ...interface{}) {
fmt.Fprintln(os.Stderr, v...)
os.Exit(1)
}
func contains(s []int, e int) bool { func contains(s []int, e int) bool {
for _, a := range s { for _, a := range s {
if a == e { if a == e {
@@ -63,6 +57,9 @@ func PrepareModeFlags() error {
OutputMode = "markdown" OutputMode = "markdown"
case OutflagOrgtable: case OutflagOrgtable:
OutputMode = "orgtbl" OutputMode = "orgtbl"
case OutflagShell:
OutputMode = "shell"
NoNumbering = true
default: default:
OutputMode = "ascii" OutputMode = "ascii"
} }
@@ -82,3 +79,13 @@ func PrepareModeFlags() error {
return nil return nil
} }
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
}

View File

@@ -31,7 +31,11 @@ func ProcessFiles(args []string) error {
} }
for _, fd := range fds { for _, fd := range fds {
printData(parseFile(fd, pattern)) data, err := parseFile(fd, pattern)
if err != nil {
return err
}
printData(&data)
} }
return nil return nil

View File

@@ -19,6 +19,7 @@ package lib
import ( import (
"bufio" "bufio"
"errors"
"fmt" "fmt"
"github.com/alecthomas/repr" "github.com/alecthomas/repr"
"io" "io"
@@ -43,7 +44,7 @@ type Tabdata struct {
way we can turn "tabular data" (with fields containing whitespaces) way we can turn "tabular data" (with fields containing whitespaces)
into real tabular data. We re-tabulate our input if you will. into real tabular data. We re-tabulate our input if you will.
*/ */
func parseFile(input io.Reader, pattern string) Tabdata { func parseFile(input io.Reader, pattern string) (Tabdata, error) {
data := Tabdata{} data := Tabdata{}
var scanner *bufio.Scanner var scanner *bufio.Scanner
@@ -65,7 +66,7 @@ func parseFile(input io.Reader, pattern string) Tabdata {
patternR, err := regexp.Compile(pattern) patternR, err := regexp.Compile(pattern)
if err != nil { if err != nil {
die(err) return data, errors.Unwrap(fmt.Errorf("Regexp pattern %s is invalid: %w", pattern, err))
} }
if !hadFirst { if !hadFirst {
@@ -145,20 +146,17 @@ func parseFile(input io.Reader, pattern string) Tabdata {
idx++ idx++
} }
if Debug {
fmt.Println()
}
data.entries = append(data.entries, values) data.entries = append(data.entries, values)
} }
} }
if scanner.Err() != nil { if scanner.Err() != nil {
die(scanner.Err()) return data, errors.Unwrap(fmt.Errorf("Regexp pattern %s is invalid: %w", pattern, scanner.Err()))
} }
if Debug { if Debug {
repr.Print(data) repr.Print(data)
} }
return data return data, nil
} }

View File

@@ -70,7 +70,12 @@ asd igig cxxxncnc
19191 EDD 1 X` 19191 EDD 1 X`
readFd := strings.NewReader(table) readFd := strings.NewReader(table)
gotdata := parseFile(readFd, "") gotdata, err := parseFile(readFd, "")
if err != nil {
t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, gotdata)
}
if !reflect.DeepEqual(data, gotdata) { if !reflect.DeepEqual(data, gotdata) {
t.Errorf("Parser returned invalid data\nExp: %+v\nGot: %+v\n", data, gotdata) t.Errorf("Parser returned invalid data\nExp: %+v\nGot: %+v\n", data, gotdata)
} }

View File

@@ -25,21 +25,24 @@ import (
"strings" "strings"
) )
func printData(data Tabdata) { func printData(data *Tabdata) {
// prepare headers // prepare headers: add numbers to headers
// FIXME: maybe do this already in parseFile()?
if !NoNumbering {
numberedHeaders := []string{} numberedHeaders := []string{}
for i, head := range data.headers { for i, head := range data.headers {
if len(Columns) > 0 { if len(Columns) > 0 {
// -c specified
if !contains(UseColumns, i+1) { if !contains(UseColumns, i+1) {
// ignore this one
continue continue
} }
} }
if NoNumbering {
numberedHeaders = append(numberedHeaders, head)
} else {
numberedHeaders = append(numberedHeaders, fmt.Sprintf("%s(%d)", head, i+1)) numberedHeaders = append(numberedHeaders, fmt.Sprintf("%s(%d)", head, i+1))
} }
data.headers = numberedHeaders
} }
data.headers = numberedHeaders
// prepare data // prepare data
if len(Columns) > 0 { if len(Columns) > 0 {
@@ -68,25 +71,17 @@ func printData(data Tabdata) {
printOrgmodeData(data) printOrgmodeData(data)
case "markdown": case "markdown":
printMarkdownData(data) printMarkdownData(data)
case "shell":
printShellData(data)
default: default:
printAsciiData(data) printAsciiData(data)
} }
} }
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
}
/* /*
Emacs org-mode compatible table (also orgtbl-mode) Emacs org-mode compatible table (also orgtbl-mode)
*/ */
func printOrgmodeData(data Tabdata) { func printOrgmodeData(data *Tabdata) {
tableString := &strings.Builder{} tableString := &strings.Builder{}
table := tablewriter.NewWriter(tableString) table := tablewriter.NewWriter(tableString)
@@ -118,7 +113,7 @@ func printOrgmodeData(data Tabdata) {
/* /*
Markdown table Markdown table
*/ */
func printMarkdownData(data Tabdata) { func printMarkdownData(data *Tabdata) {
table := tablewriter.NewWriter(os.Stdout) table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(data.headers) table.SetHeader(data.headers)
@@ -136,7 +131,7 @@ func printMarkdownData(data Tabdata) {
/* /*
Simple ASCII table without any borders etc, just like the input we expect Simple ASCII table without any borders etc, just like the input we expect
*/ */
func printAsciiData(data Tabdata) { func printAsciiData(data *Tabdata) {
table := tablewriter.NewWriter(os.Stdout) table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(data.headers) table.SetHeader(data.headers)
@@ -164,7 +159,7 @@ func printAsciiData(data Tabdata) {
/* /*
We simulate the \x command of psql (the PostgreSQL client) We simulate the \x command of psql (the PostgreSQL client)
*/ */
func printExtendedData(data Tabdata) { func printExtendedData(data *Tabdata) {
// needed for data output // needed for data output
format := fmt.Sprintf("%%%ds: %%s\n", data.maxwidthHeader) // FIXME: re-calculate if -c has been set format := fmt.Sprintf("%%%ds: %%s\n", data.maxwidthHeader) // FIXME: re-calculate if -c has been set
@@ -186,3 +181,26 @@ func printExtendedData(data Tabdata) {
} }
} }
} }
/*
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
for i, value := range entry {
if len(Columns) > 0 {
if !contains(UseColumns, i+1) {
continue
}
}
fmt.Printf("%s=\"%s\" ", data.headers[idx], value)
idx++
}
fmt.Println()
}
}
}

View File

@@ -54,8 +54,13 @@ asd igig cxxxncnc
for mode, expect := range expects { for mode, expect := range expects {
OutputMode = mode OutputMode = mode
fd := strings.NewReader(table) fd := strings.NewReader(table)
data := parseFile(fd, "") data, err := parseFile(fd, "")
printData(data)
if err != nil {
t.Errorf("Parser returned error: %s\nData processed so far: %+v", err, data)
}
printData(&data)
buf := make([]byte, 1024) buf := make([]byte, 1024)
n, err := r.Read(buf) n, err := r.Read(buf)

View File

@@ -74,7 +74,7 @@ The numbering can be suppressed by using the B<-n> option.
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.
?head2 OUTPUT MODES =head2 OUTPUT MODES
There might be cases when the tabular output of a program is way too 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 large for your current terminal but you still need to see every
@@ -83,7 +83,7 @@ usefull which enables I<extended mode>. In this mode, each row will be
printed vertically, header left, value right, aligned by the field printed vertically, header left, value right, aligned by the field
widths. Here's an example: widths. Here's an example:
kubectl get pods | ./tablizer -X kubectl get pods | ./tablizer -o extended
NAME: repldepl-7bcd8d5b64-7zq4l NAME: repldepl-7bcd8d5b64-7zq4l
READY: 1/1 READY: 1/1
STATUS: Running STATUS: Running
@@ -93,6 +93,17 @@ widths. Here's an example:
You can of course still use a regex to reduce the number of rows You can of course still use a regex to reduce the number of rows
displayed. 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 Beside normal ascii mode (the default) and extended mode there are
more output modes available: B<orgtbl> which prints an Emacs org-mode more output modes available: B<orgtbl> which prints an Emacs org-mode
table and B<markdown> which prints a Markdown table. table and B<markdown> which prints a Markdown table.