github.com/wangyougui/gf/v2@v2.6.5/os/gproc/gproc_shell.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/wangyougui/gf.
     6  
     7  package gproc
     8  
     9  import (
    10  	"bytes"
    11  	"context"
    12  	"fmt"
    13  	"io"
    14  	"runtime"
    15  
    16  	"go.opentelemetry.io/otel"
    17  	"go.opentelemetry.io/otel/propagation"
    18  
    19  	"github.com/wangyougui/gf/v2/os/gfile"
    20  	"github.com/wangyougui/gf/v2/text/gstr"
    21  )
    22  
    23  // Shell executes command `cmd` synchronously with given input pipe `in` and output pipe `out`.
    24  // The command `cmd` reads the input parameters from input pipe `in`, and writes its output automatically
    25  // to output pipe `out`.
    26  func Shell(ctx context.Context, cmd string, out io.Writer, in io.Reader) error {
    27  	p := NewProcess(
    28  		getShell(),
    29  		append([]string{getShellOption()}, parseCommand(cmd)...),
    30  	)
    31  	p.Stdin = in
    32  	p.Stdout = out
    33  	return p.Run(ctx)
    34  }
    35  
    36  // ShellRun executes given command `cmd` synchronously and outputs the command result to the stdout.
    37  func ShellRun(ctx context.Context, cmd string) error {
    38  	p := NewProcess(
    39  		getShell(),
    40  		append([]string{getShellOption()}, parseCommand(cmd)...),
    41  	)
    42  	return p.Run(ctx)
    43  }
    44  
    45  // ShellExec executes given command `cmd` synchronously and returns the command result.
    46  func ShellExec(ctx context.Context, cmd string, environment ...[]string) (result string, err error) {
    47  	var (
    48  		buf = bytes.NewBuffer(nil)
    49  		p   = NewProcess(
    50  			getShell(),
    51  			append([]string{getShellOption()}, parseCommand(cmd)...),
    52  			environment...,
    53  		)
    54  	)
    55  	p.Stdout = buf
    56  	p.Stderr = buf
    57  	err = p.Run(ctx)
    58  	result = buf.String()
    59  	return
    60  }
    61  
    62  // parseCommand parses command `cmd` into slice arguments.
    63  //
    64  // Note that it just parses the `cmd` for "cmd.exe" binary in windows, but it is not necessary
    65  // parsing the `cmd` for other systems using "bash"/"sh" binary.
    66  func parseCommand(cmd string) (args []string) {
    67  	if runtime.GOOS != "windows" {
    68  		return []string{cmd}
    69  	}
    70  	// Just for "cmd.exe" in windows.
    71  	var argStr string
    72  	var firstChar, prevChar, lastChar1, lastChar2 byte
    73  	array := gstr.SplitAndTrim(cmd, " ")
    74  	for _, v := range array {
    75  		if len(argStr) > 0 {
    76  			argStr += " "
    77  		}
    78  		firstChar = v[0]
    79  		lastChar1 = v[len(v)-1]
    80  		lastChar2 = 0
    81  		if len(v) > 1 {
    82  			lastChar2 = v[len(v)-2]
    83  		}
    84  		if prevChar == 0 && (firstChar == '"' || firstChar == '\'') {
    85  			// It should remove the first quote char.
    86  			argStr += v[1:]
    87  			prevChar = firstChar
    88  		} else if prevChar != 0 && lastChar2 != '\\' && lastChar1 == prevChar {
    89  			// It should remove the last quote char.
    90  			argStr += v[:len(v)-1]
    91  			args = append(args, argStr)
    92  			argStr = ""
    93  			prevChar = 0
    94  		} else if len(argStr) > 0 {
    95  			argStr += v
    96  		} else {
    97  			args = append(args, v)
    98  		}
    99  	}
   100  	return
   101  }
   102  
   103  // getShell returns the shell command depending on current working operating system.
   104  // It returns "cmd.exe" for windows, and "bash" or "sh" for others.
   105  func getShell() string {
   106  	switch runtime.GOOS {
   107  	case "windows":
   108  		return SearchBinary("cmd.exe")
   109  
   110  	default:
   111  		// Check the default binary storage path.
   112  		if gfile.Exists("/bin/bash") {
   113  			return "/bin/bash"
   114  		}
   115  		if gfile.Exists("/bin/sh") {
   116  			return "/bin/sh"
   117  		}
   118  		// Else search the env PATH.
   119  		path := SearchBinary("bash")
   120  		if path == "" {
   121  			path = SearchBinary("sh")
   122  		}
   123  		return path
   124  	}
   125  }
   126  
   127  // getShellOption returns the shell option depending on current working operating system.
   128  // It returns "/c" for windows, and "-c" for others.
   129  func getShellOption() string {
   130  	switch runtime.GOOS {
   131  	case "windows":
   132  		return "/c"
   133  
   134  	default:
   135  		return "-c"
   136  	}
   137  }
   138  
   139  // tracingEnvFromCtx converts OpenTelemetry propagation data as environment variables.
   140  func tracingEnvFromCtx(ctx context.Context) []string {
   141  	var (
   142  		a = make([]string, 0)
   143  		m = make(map[string]string)
   144  	)
   145  	otel.GetTextMapPropagator().Inject(ctx, propagation.MapCarrier(m))
   146  	for k, v := range m {
   147  		a = append(a, fmt.Sprintf(`%s=%s`, k, v))
   148  	}
   149  	return a
   150  }