github.com/gogf/gf@v1.16.9/os/gproc/gproc.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/gogf/gf. 6 7 // Package gproc implements management and communication for processes. 8 package gproc 9 10 import ( 11 "bytes" 12 "github.com/gogf/gf/os/genv" 13 "github.com/gogf/gf/text/gstr" 14 "io" 15 "os" 16 "runtime" 17 "time" 18 19 "github.com/gogf/gf/os/gfile" 20 "github.com/gogf/gf/util/gconv" 21 ) 22 23 const ( 24 envKeyPPid = "GPROC_PPID" 25 ) 26 27 var ( 28 processPid = os.Getpid() // processPid is the pid of current process. 29 processStartTime = time.Now() // processStartTime is the start time of current process. 30 ) 31 32 // Pid returns the pid of current process. 33 func Pid() int { 34 return processPid 35 } 36 37 // PPid returns the custom parent pid if exists, or else it returns the system parent pid. 38 func PPid() int { 39 if !IsChild() { 40 return Pid() 41 } 42 ppidValue := os.Getenv(envKeyPPid) 43 if ppidValue != "" && ppidValue != "0" { 44 return gconv.Int(ppidValue) 45 } 46 return PPidOS() 47 } 48 49 // PPidOS returns the system parent pid of current process. 50 // Note that the difference between PPidOS and PPid function is that the PPidOS returns 51 // the system ppid, but the PPid functions may return the custom pid by gproc if the custom 52 // ppid exists. 53 func PPidOS() int { 54 return os.Getppid() 55 } 56 57 // IsChild checks and returns whether current process is a child process. 58 // A child process is forked by another gproc process. 59 func IsChild() bool { 60 ppidValue := os.Getenv(envKeyPPid) 61 return ppidValue != "" && ppidValue != "0" 62 } 63 64 // SetPPid sets custom parent pid for current process. 65 func SetPPid(ppid int) error { 66 if ppid > 0 { 67 return os.Setenv(envKeyPPid, gconv.String(ppid)) 68 } else { 69 return os.Unsetenv(envKeyPPid) 70 } 71 } 72 73 // StartTime returns the start time of current process. 74 func StartTime() time.Time { 75 return processStartTime 76 } 77 78 // Uptime returns the duration which current process has been running 79 func Uptime() time.Duration { 80 return time.Now().Sub(processStartTime) 81 } 82 83 // Shell executes command <cmd> synchronizingly with given input pipe <in> and output pipe <out>. 84 // The command <cmd> reads the input parameters from input pipe <in>, and writes its output automatically 85 // to output pipe <out>. 86 func Shell(cmd string, out io.Writer, in io.Reader) error { 87 p := NewProcess(getShell(), append([]string{getShellOption()}, parseCommand(cmd)...)) 88 p.Stdin = in 89 p.Stdout = out 90 return p.Run() 91 } 92 93 // ShellRun executes given command <cmd> synchronizingly and outputs the command result to the stdout. 94 func ShellRun(cmd string) error { 95 p := NewProcess(getShell(), append([]string{getShellOption()}, parseCommand(cmd)...)) 96 return p.Run() 97 } 98 99 // ShellExec executes given command <cmd> synchronizingly and returns the command result. 100 func ShellExec(cmd string, environment ...[]string) (string, error) { 101 buf := bytes.NewBuffer(nil) 102 p := NewProcess(getShell(), append([]string{getShellOption()}, parseCommand(cmd)...), environment...) 103 p.Stdout = buf 104 p.Stderr = buf 105 err := p.Run() 106 return buf.String(), err 107 } 108 109 // parseCommand parses command <cmd> into slice arguments. 110 // 111 // Note that it just parses the <cmd> for "cmd.exe" binary in windows, but it is not necessary 112 // parsing the <cmd> for other systems using "bash"/"sh" binary. 113 func parseCommand(cmd string) (args []string) { 114 if runtime.GOOS != "windows" { 115 return []string{cmd} 116 } 117 // Just for "cmd.exe" in windows. 118 var argStr string 119 var firstChar, prevChar, lastChar1, lastChar2 byte 120 array := gstr.SplitAndTrim(cmd, " ") 121 for _, v := range array { 122 if len(argStr) > 0 { 123 argStr += " " 124 } 125 firstChar = v[0] 126 lastChar1 = v[len(v)-1] 127 lastChar2 = 0 128 if len(v) > 1 { 129 lastChar2 = v[len(v)-2] 130 } 131 if prevChar == 0 && (firstChar == '"' || firstChar == '\'') { 132 // It should remove the first quote char. 133 argStr += v[1:] 134 prevChar = firstChar 135 } else if prevChar != 0 && lastChar2 != '\\' && lastChar1 == prevChar { 136 // It should remove the last quote char. 137 argStr += v[:len(v)-1] 138 args = append(args, argStr) 139 argStr = "" 140 prevChar = 0 141 } else if len(argStr) > 0 { 142 argStr += v 143 } else { 144 args = append(args, v) 145 } 146 } 147 return 148 } 149 150 // getShell returns the shell command depending on current working operation system. 151 // It returns "cmd.exe" for windows, and "bash" or "sh" for others. 152 func getShell() string { 153 switch runtime.GOOS { 154 case "windows": 155 return SearchBinary("cmd.exe") 156 default: 157 // Check the default binary storage path. 158 if gfile.Exists("/bin/bash") { 159 return "/bin/bash" 160 } 161 if gfile.Exists("/bin/sh") { 162 return "/bin/sh" 163 } 164 // Else search the env PATH. 165 path := SearchBinary("bash") 166 if path == "" { 167 path = SearchBinary("sh") 168 } 169 return path 170 } 171 } 172 173 // getShellOption returns the shell option depending on current working operation system. 174 // It returns "/c" for windows, and "-c" for others. 175 func getShellOption() string { 176 switch runtime.GOOS { 177 case "windows": 178 return "/c" 179 default: 180 return "-c" 181 } 182 } 183 184 // SearchBinary searches the binary <file> in current working folder and PATH environment. 185 func SearchBinary(file string) string { 186 // Check if it's absolute path of exists at current working directory. 187 if gfile.Exists(file) { 188 return file 189 } 190 return SearchBinaryPath(file) 191 } 192 193 // SearchBinaryPath searches the binary <file> in PATH environment. 194 func SearchBinaryPath(file string) string { 195 array := ([]string)(nil) 196 switch runtime.GOOS { 197 case "windows": 198 envPath := genv.Get("PATH", genv.Get("Path")) 199 if gstr.Contains(envPath, ";") { 200 array = gstr.SplitAndTrim(envPath, ";") 201 } else if gstr.Contains(envPath, ":") { 202 array = gstr.SplitAndTrim(envPath, ":") 203 } 204 if gfile.Ext(file) != ".exe" { 205 file += ".exe" 206 } 207 default: 208 array = gstr.SplitAndTrim(genv.Get("PATH"), ":") 209 } 210 if len(array) > 0 { 211 path := "" 212 for _, v := range array { 213 path = v + gfile.Separator + file 214 if gfile.Exists(path) && gfile.IsFile(path) { 215 return path 216 } 217 } 218 } 219 return "" 220 }