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 }