github.com/madlambda/nash@v0.2.2-0.20230113003044-f2284521680b/internal/sh/cmd.go (about) 1 package sh 2 3 import ( 4 "io" 5 "os/exec" 6 "path/filepath" 7 8 "github.com/madlambda/nash/ast" 9 "github.com/madlambda/nash/errors" 10 "github.com/madlambda/nash/sh" 11 ) 12 13 type ( 14 // Cmd is a nash command. It has maps of input and output file 15 // descriptors that can be set by SetInputfd and SetOutputfd. 16 // This can be used to pipe execution of Cmd commands. 17 Cmd struct { 18 *exec.Cmd 19 20 argExprs []ast.Expr 21 } 22 23 // errCmdNotFound is an error indicating the command wasn't found. 24 errCmdNotFound struct { 25 *errors.NashError 26 } 27 ) 28 29 func newCmdNotFound(format string, arg ...interface{}) error { 30 e := &errCmdNotFound{ 31 NashError: errors.NewError(format, arg...), 32 } 33 34 return e 35 } 36 37 func (e *errCmdNotFound) NotFound() bool { 38 return true 39 } 40 41 func NewCmd(name string) (*Cmd, error) { 42 var ( 43 err error 44 cmdPath = name 45 ) 46 47 cmd := Cmd{} 48 49 if !filepath.IsAbs(name) { 50 cmdPath, err = exec.LookPath(name) 51 52 if err != nil { 53 return nil, newCmdNotFound(err.Error()) 54 } 55 } 56 57 cmd.Cmd = &exec.Cmd{ 58 Path: cmdPath, 59 } 60 61 return &cmd, nil 62 } 63 64 func (c *Cmd) Stdin() io.Reader { return c.Cmd.Stdin } 65 func (c *Cmd) Stdout() io.Writer { return c.Cmd.Stdout } 66 func (c *Cmd) Stderr() io.Writer { return c.Cmd.Stderr } 67 68 func (c *Cmd) SetStdin(in io.Reader) { c.Cmd.Stdin = in } 69 func (c *Cmd) SetStdout(out io.Writer) { c.Cmd.Stdout = out } 70 func (c *Cmd) SetStderr(err io.Writer) { c.Cmd.Stderr = err } 71 72 func (c *Cmd) SetArgs(nodeArgs []sh.Obj) error { 73 args := make([]string, 1, len(nodeArgs)+1) 74 args[0] = c.Path 75 76 for _, obj := range nodeArgs { 77 if obj.Type() == sh.StringType { 78 objstr := obj.(*sh.StrObj) 79 args = append(args, objstr.Str()) 80 } else if obj.Type() == sh.ListType { 81 objlist := obj.(*sh.ListObj) 82 values := objlist.List() 83 84 for _, l := range values { 85 if l.Type() != sh.StringType { 86 return errors.NewError("Command arguments requires string or list of strings. But received '%v'", l.String()) 87 } 88 89 lstr := l.(*sh.StrObj) 90 args = append(args, lstr.Str()) 91 } 92 } else if obj.Type() == sh.FnType { 93 return errors.NewError("Function cannot be passed as argument to commands.") 94 } else { 95 return errors.NewError("Invalid command argument '%v'", obj) 96 } 97 } 98 99 c.Cmd.Args = args 100 return nil 101 } 102 103 func (c *Cmd) Args() []ast.Expr { return c.argExprs } 104 105 func (c *Cmd) SetEnviron(env []string) { 106 c.Cmd.Env = env 107 } 108 109 func (c *Cmd) Wait() error { 110 err := c.Cmd.Wait() 111 112 if err != nil { 113 return err 114 } 115 116 return nil 117 } 118 119 func (c *Cmd) Start() error { 120 err := c.Cmd.Start() 121 if err != nil { 122 return err 123 } 124 return nil 125 } 126 127 func (c *Cmd) Results() []sh.Obj { return nil }