gproc_shell.go 3.8 KB

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