gproc.go 5.9 KB

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