github.com/NeowayLabs/nash@v0.2.2-0.20200127205349-a227041ffd50/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 }