Skip to content

Commit

Permalink
generalize "write" -- file output + shell pipes
Browse files Browse the repository at this point in the history
  • Loading branch information
bmatsuo committed Jul 1, 2014
1 parent 900e8a4 commit 6623876
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 34 deletions.
133 changes: 102 additions & 31 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,53 @@ func cmdLoad(jq *JQShell, flags *CmdFlags) error {
return nil
}

func cmdPipeShell(jq *JQShell, flags *CmdFlags) error {
flags.ArgSet("command")
color := flags.Bool("-color", false, "pass colorized json to command")
err := flags.Parse(nil)
if IsHelp(err) {
return nil
}
if err != nil {
return err
}
args := flags.Args()
if len(args) == 0 {
return fmt.Errorf("missing command")
}
shell := os.Getenv("SHELL")
if shell == "" {
shell = "bash"
}
shcmd := []string{shell, "-c", args[0]}
cmd := exec.Command(shell, shcmd[1:]...)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
stdin, err := cmd.StdinPipe()
if err != nil {
return fmt.Errorf("creating pipe: %v", err)
}
err = cmd.Start()
waiterr := make(chan error, 1)
go func() {
waiterr <- cmd.Wait()
stdin.Close()
}()
if err != nil {
return ExecError{shcmd, err}
}
_, _, err = cmdWrite_io(jq, stdin, *color, nil)
if err != nil {
<-waiterr
return err
}
err = <-waiterr
if err != nil {
return ExecError{shcmd, err}
}
return nil
}

func cmdWrite(jq *JQShell, flags *CmdFlags) error {
flags.ArgSet("[filename]")
err := flags.Parse(nil)
Expand All @@ -256,40 +303,64 @@ func cmdWrite(jq *JQShell, flags *CmdFlags) error {
}
args := flags.Args()
if len(args) == 0 {
r, err := jq.Input()
if err != nil {
return err
}
defer r.Close()
w, errch := Page(nil)
select {
case err := <-errch:
return err
default:
break
}
pageerr := make(chan error, 1)
stop := make(chan struct{})
go func() {
err := <-errch
close(stop)
if err != nil {
pageerr <- err
}
close(pageerr)
}()
_, _, err = Execute(w, os.Stderr, r, stop, jq.bin, jq.Stack)
w.Close()
return cmdWrite_page(jq)
}
return cmdWrite_file(jq, args[0])
}

func cmdWrite_page(jq *JQShell) error {
w, errch := Page(nil)
select {
case err := <-errch:
return err
default:
break
}
pageerr := make(chan error, 1)
stop := make(chan struct{})
go func() {
err := <-errch
close(stop)
if err != nil {
return ExecError{[]string{"jq"}, err}
}
pageErr := <-pageerr
if pageErr != nil {
jq.log("pager: ", pageErr)
pageerr <- err
}
return nil
close(pageerr)
}()
_, _, err := cmdWrite_io(jq, w, true, stop)
if err != nil {
return err
}
return fmt.Errorf("file output not allowed")
pageErr := <-pageerr
if pageErr != nil {
jq.log("pager: ", pageErr)
}
return nil
}

func cmdWrite_file(jq *JQShell, filename string) error {
f, err := os.Create(filename)
if err != nil {
return err
}
nout, _, err := cmdWrite_io(jq, f, false, nil)
if err == nil {
jq.Log.Printf("%d bytes written to %q", nout, filename)
}
return err
}

func cmdWrite_io(jq *JQShell, w io.WriteCloser, color bool, stop chan struct{}) (int64, int64, error) {
r, err := jq.Input()
if err != nil {
return 0, 0, err
}
defer r.Close()
defer w.Close()
nout, nerr, err := Execute(w, os.Stderr, r, stop, jq.bin, color, jq.Stack)
if err != nil {
return nout, nerr, ExecError{[]string{"jq"}, err}
}
return nout, nerr, err
}

func cmdRaw(jq *JQShell, flags *CmdFlags) error {
Expand Down
9 changes: 7 additions & 2 deletions jq.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,18 @@ func (w *writeCounter) Write(bs []byte) (int, error) {
return n, err
}

func Execute(outw, errw io.Writer, in io.Reader, stop <-chan struct{}, jq string, s *JQStack) (int64, int64, error) {
func Execute(outw, errw io.Writer, in io.Reader, stop <-chan struct{}, jq string, color bool, s *JQStack) (int64, int64, error) {
if jq == "" {
jq = "jq"
}
outcounter := &writeCounter{0, outw}
errcounter := &writeCounter{0, errw}
cmd := exec.Command(jq, "-C", JoinFilter(s)) // TODO test if stdout is a terminal
var args []string
if color {
args = append(args, "--color-output")
}
args = append(args, JoinFilter(s))
cmd := exec.Command(jq, args...)
cmd.Stdin = in
cmd.Stdout = outcounter
cmd.Stderr = errcounter
Expand Down
3 changes: 2 additions & 1 deletion jqsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ func NewJQShell(bin string, sh ShellReader) *JQShell {
jq.lib.Register("script", JQShellCommandFunc(cmdScript))
jq.lib.Register("load", JQShellCommandFunc(cmdLoad))
jq.lib.Register("exec", JQShellCommandFunc(cmdExec))
jq.lib.Register("sh", JQShellCommandFunc(cmdPipeShell))
jq.lib.Register("write", JQShellCommandFunc(cmdWrite))
jq.lib.Register("raw", JQShellCommandFunc(cmdRaw))
jq.lib.Register("quit", JQShellCommandFunc(cmdQuit))
Expand Down Expand Up @@ -299,7 +300,7 @@ func (jq *JQShell) loop() {
jq.Log.Print(err)
} else if len(cmd.cmd) == 0 {
jq.Log.Println("empty command")
} else if cmd.cmd[0] != "write" && cmd.cmd[0] != "raw" && cmd.cmd[0] != "filter" && cmd.cmd[0] != "script" && cmd.cmd[0] != "help" {
} else if cmd.cmd[0] != "write" && cmd.cmd[0] != "raw" && cmd.cmd[0] != "filter" && cmd.cmd[0] != "script" && cmd.cmd[0] != "help" && cmd.cmd[0] != "sh" {
// TODO clean this up. (cmdPushInteractive, cmdPeek)
err := jq.execute([]string{"write"}, nil)
if err != nil {
Expand Down

0 comments on commit 6623876

Please sign in to comment.