gproc.go 5.9 KB

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