mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-18 13:01:11 +01:00
4
TODO
4
TODO
@@ -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
|
|
||||||
|
|
||||||
|
|||||||
@@ -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)")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)"
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
15
tablizer.pod
15
tablizer.pod
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user