github.com/coveo/gotemplate@v2.7.7+incompatible/utils/exec.go (about)

     1  package utils
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"os/exec"
     8  	"regexp"
     9  	"runtime"
    10  	"strings"
    11  )
    12  
    13  // https://regex101.com/r/ykVKPt/6
    14  var shebang = regexp.MustCompile(`(?sm)^(?:\s*#!\s*(?P<program>[^\s]*)[ \t]*(?P<app>[^\s]*)?\s*$)?\s*(?P<source>.*)`)
    15  
    16  // IsShebangScript determines if the supplied code has a Shebang definition #! program subprogram
    17  func IsShebangScript(content string) bool {
    18  	matches := shebang.FindStringSubmatch(strings.TrimSpace(content))
    19  	return len(matches) > 0 && matches[1] != ""
    20  }
    21  
    22  // ScriptParts splits up the supplied content into program, subprogram and source if the content matches Shebang defintion
    23  func ScriptParts(content string) (program, subprogram, source string) {
    24  	matches := shebang.FindStringSubmatch(strings.TrimSpace(content))
    25  	if len(matches) > 0 {
    26  		program = matches[1]
    27  		subprogram = matches[2]
    28  		source = matches[3]
    29  	} else {
    30  		source = content
    31  	}
    32  	return
    33  }
    34  
    35  // GetCommandFromFile returns an exec.Cmd structure to run the supplied script file
    36  func GetCommandFromFile(filename string, args ...interface{}) (cmd *exec.Cmd, err error) {
    37  	script, err := ioutil.ReadFile(filename)
    38  	if err != nil {
    39  		return
    40  	}
    41  
    42  	executer, delegate, command := ScriptParts(strings.TrimSpace(string(script)))
    43  
    44  	strArgs := GlobFunc(args...)
    45  
    46  	if executer != "" {
    47  		command = executer
    48  		strArgs = append([]string{filename}, strArgs...)
    49  		if delegate != "" {
    50  			strArgs = append([]string{delegate}, strArgs...)
    51  		}
    52  	} else if _, errPath := exec.LookPath(command); errPath != nil {
    53  		if strings.Contains(command, " ") {
    54  			// The command is a string that should be splitted up into several parts
    55  			split := strings.Split(command, " ")
    56  			command = split[0]
    57  			strArgs = append(split[1:], strArgs...)
    58  		} else {
    59  			// The command does not exist
    60  			return
    61  		}
    62  	}
    63  
    64  	cmd = exec.Command(command, strArgs...)
    65  	return
    66  }
    67  
    68  // GetCommandFromString returns an exec.Cmd structure to run the supplied command
    69  func GetCommandFromString(script string, args ...interface{}) (cmd *exec.Cmd, tempFile string, err error) {
    70  	if executer, delegate, command := ScriptParts(strings.TrimSpace(script)); executer != "" {
    71  		cmd, tempFile, err = saveTempFile(fmt.Sprintf("#! %s %s\n%s", executer, delegate, command), args...)
    72  	} else {
    73  		strArgs := GlobFunc(args...)
    74  		if _, err = exec.LookPath(command); err == nil {
    75  			cmd = exec.Command(command, strArgs...)
    76  			return
    77  		}
    78  		if IsCommand(command) {
    79  			// The command does just not exist, we return the error
    80  			return
    81  		}
    82  
    83  		if cmd, tempFile, err = saveTempFile(fmt.Sprintf("#! %s\n%s", getDefaultShell(), command), args...); err != nil {
    84  			return
    85  		}
    86  	}
    87  	return
    88  }
    89  
    90  // IsCommand ensures that the supplied command does not contain any shell specific characters
    91  func IsCommand(command string) bool {
    92  	return !strings.ContainsAny(command, " \t|&$,;(){}<>[]")
    93  }
    94  
    95  func saveTempFile(content string, args ...interface{}) (cmd *exec.Cmd, fileName string, err error) {
    96  	var temp *os.File
    97  	if temp, err = ioutil.TempFile("", "exec_"); err != nil {
    98  		return
    99  	}
   100  
   101  	if _, err = temp.WriteString(content); err != nil {
   102  		return
   103  	}
   104  	temp.Close()
   105  	fileName = temp.Name()
   106  	cmd, err = GetCommandFromFile(fileName, args...)
   107  	return
   108  }
   109  
   110  func getDefaultShell() string {
   111  	if defaultShell != "" {
   112  		return defaultShell
   113  	}
   114  	defShells := []string{"bash", "sh", "zsh", "ksh"}
   115  	if runtime.GOOS == "windows" {
   116  		defShells = []string{"powershell", "pwsh", "cmd"}
   117  	}
   118  	for _, sh := range defShells {
   119  		if sh, err := exec.LookPath(sh); err == nil {
   120  			defaultShell = sh
   121  			break
   122  		}
   123  	}
   124  	return defaultShell
   125  }
   126  
   127  var defaultShell string
   128  
   129  // GetEnv returns the environment variable value or defaultValue if it does not exist.
   130  func GetEnv(varName, defaultValue string) string {
   131  	if value, exist := os.LookupEnv(varName); exist {
   132  		return value
   133  	}
   134  	return defaultValue
   135  }