项目中通过 exec.Command 执行的 shell 脚本获取不到系统变量
这里参考了 https://blog.csdn.net/qq_36874881/article/details/78234005
下面是我写的一段测试代码及输出:
package main import ( "bufio" "fmt" "io" "os/exec" ) func main() { command := "whoami && pwd && echo $UID && echo $USER && export" cmd := exec.Command("/bin/bash", "-c", command) print(cmd) compilerPath := "/data/web/CodeCompiler/CodeCompiler" compilerCommand := "/bin/bash ../example-linux.sh" command = "source /etc/profile && cd " + compilerPath + " && " + compilerCommand cmd = exec.Command("/bin/bash", "-c", command) print(cmd) } func print(cmd *exec.Cmd) { fmt.Println(cmd.Args) stdout, err := cmd.StdoutPipe() if err != nil { fmt.Println(err) } cmd.Start() //创建一个流来读取管道内内容,这里逻辑是通过一行一行的读取的 reader := bufio.NewReader(stdout) //实时循环读取输出流中的一行内容 for { line, err2 := reader.ReadString('\n') if err2 != nil || io.EOF == err2 { break } fmt.Println(line) } cmd.Wait() }执行 go run main.go 输出如下:
[/bin/bash -c whoami && pwd && echo $UID && echo $USER && echo $HOME && export] root /data/web/CodeCompiler 0 root /root declare -x GO111MODULE="off" declare -x GOBIN="/data/app/go/bin" declare -x GOCACHE="/root/.cache/go-build" declare -x GOPATH="/data/app/go:/data/app/Pressure_Test/current/deps:/data/app/Pressure_Test/current" declare -x HISTCONTROL="ignoredups" declare -x HISTSIZE="1000" declare -x HOME="/root" declare -x HOSTNAME="jszx_vm_10_0_150_180" declare -x LANG="en_US.UTF-8" declare -x LC_CTYPE="UTF-8" declare -x LESSOPEN="||/usr/bin/lesspipe.sh %s" declare -x LOADEDMODULES="" declare -x LOGNAME="root" declare -x LS_COLORS="rs=0:di=38;5;27:ln=38;5;51:mh=44;38;5;15:pi=40;38;5;11:so=38;5;13:do=38;5;5:bd=48;5;232;38;5;11:cd=48;5;232;38;5;3:or=48;5;232;38;5;9:mi=05;48;5;232;38;5;15:su=48;5;196;38;5;15:sg=48;5;11;38;5;16:ca=48;5;196;38;5;226:tw=48;5;10;38;5;16:ow=48;5;10;38;5;21:st=48;5;21;38;5;15:ex=38;5;34:*.tar=38;5;9:*.tgz=38;5;9:*.arc=38;5;9:*.arj=38;5;9:*.taz=38;5;9:*.lha=38;5;9:*.lz4=38;5;9:*.lzh=38;5;9:*.lzma=38;5;9:*.tlz=38;5;9:*.txz=38;5;9:*.tzo=38;5;9:*.t7z=38;5;9:*.zip=38;5;9:*.z=38;5;9:*.Z=38;5;9:*.dz=38;5;9:*.gz=38;5;9:*.lrz=38;5;9:*.lz=38;5;9:*.lzo=38;5;9:*.xz=38;5;9:*.bz2=38;5;9:*.bz=38;5;9:*.tbz=38;5;9:*.tbz2=38;5;9:*.tz=38;5;9:*.deb=38;5;9:*.rpm=38;5;9:*.jar=38;5;9:*.war=38;5;9:*.ear=38;5;9:*.sar=38;5;9:*.rar=38;5;9:*.alz=38;5;9:*.ace=38;5;9:*.zoo=38;5;9:*.cpio=38;5;9:*.7z=38;5;9:*.rz=38;5;9:*.cab=38;5;9:*.jpg=38;5;13:*.jpeg=38;5;13:*.gif=38;5;13:*.bmp=38;5;13:*.pbm=38;5;13:*.pgm=38;5;13:*.ppm=38;5;13:*.tga=38;5;13:*.xbm=38;5;13:*.xpm=38;5;13:*.tif=38;5;13:*.tiff=38;5;13:*.png=38;5;13:*.svg=38;5;13:*.svgz=38;5;13:*.mng=38;5;13:*.pcx=38;5;13:*.mov=38;5;13:*.mpg=38;5;13:*.mpeg=38;5;13:*.m2v=38;5;13:*.mkv=38;5;13:*.webm=38;5;13:*.ogm=38;5;13:*.mp4=38;5;13:*.m4v=38;5;13:*.mp4v=38;5;13:*.vob=38;5;13:*.qt=38;5;13:*.nuv=38;5;13:*.wmv=38;5;13:*.asf=38;5;13:*.rm=38;5;13:*.rmvb=38;5;13:*.flc=38;5;13:*.avi=38;5;13:*.fli=38;5;13:*.flv=38;5;13:*.gl=38;5;13:*.dl=38;5;13:*.xcf=38;5;13:*.xwd=38;5;13:*.yuv=38;5;13:*.cgm=38;5;13:*.emf=38;5;13:*.axv=38;5;13:*.anx=38;5;13:*.ogv=38;5;13:*.ogx=38;5;13:*.aac=38;5;45:*.au=38;5;45:*.flac=38;5;45:*.mid=38;5;45:*.midi=38;5;45:*.mka=38;5;45:*.mp3=38;5;45:*.mpc=38;5;45:*.ogg=38;5;45:*.ra=38;5;45:*.wav=38;5;45:*.axa=38;5;45:*.oga=38;5;45:*.spx=38;5;45:*.xspf=38;5;45:" declare -x MAIL="/var/spool/mail/root" declare -x MODULEPATH="/usr/share/Modules/modulefiles:/etc/modulefiles" declare -x MODULESHOME="/usr/share/Modules" declare -x OLDPWD declare -x PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/go/bin:/data/app/go/bin:/root/bin:/data/app/go/bin" declare -x PWD="/data/web/CodeCompiler" declare -x SHELL="/bin/bash" declare -x SHLVL="2" declare -x SSH_CLIENT="192.168.11.210 59771 22" declare -x SSH_CONNECTION="192.168.11.210 59771 10.0.150.180 22" declare -x SSH_TTY="/dev/pts/3" declare -x TERM="xterm-256color" declare -x USER="root" declare -x XDG_RUNTIME_DIR="/run/user/0" declare -x XDG_SESSION_ID="25848" [/bin/bash -c source /etc/profile && cd /data/web/CodeCompiler/CodeCompiler && /bin/bash ../example-linux.sh] /root ok这个输出很正常,没有问题
项目使用了 gin 框架,加了一个测试接口
//测试接口 g.GET("/api/test", util.Test) //测试函数 func Test(c *gin.Context) { command := "whoami && pwd && echo $UID && echo $USER && echo $HOME && export" cmd := exec.Command("/bin/bash", "-c", command) print(cmd) //这段是我项目中实际调用的shell脚本,也是出问题的地方 compilerPath := "/data/web/CodeCompiler/CodeCompiler" compilerCommand := "/bin/bash ../example-linux.sh" command = "source /etc/profile && cd " + compilerPath + " && " + compilerCommand cmd = exec.Command("/bin/bash", "-c", command) print(cmd) app.RespSuccess(c, gin.H{}) } func print(cmd *exec.Cmd) { fmt.Println(cmd.Args) stdout, err := cmd.StdoutPipe() if err != nil { fmt.Println(err) } cmd.Start() //创建一个流来读取管道内内容,这里逻辑是通过一行一行的读取的 reader := bufio.NewReader(stdout) //实时循环读取输出流中的一行内容 for { line, err2 := reader.ReadString('\n') if err2 != nil || io.EOF == err2 { break } fmt.Println(line) } cmd.Wait() }本地编译运行项目,访问接口 /api/test 查看控制台输出,和我直接执行 go run main.go 输出一致
对比 step3 和 step1 中的输出,我们发现缺少了很多环境变量;其中 /bin/bash -c source /etc/profile && cd /data/web/CodeCompiler/CodeCompiler && /bin/bash ../example-linux.sh 的第一个输出 nil 是我调用的 example-linux.sh 脚本执行的一段 lua 脚本中的 os.getenv 'HOME' 的返回,也是这次要排查的问题;
查看了项目的编排,发现我们的项目在测试环境是通过 supervisor 管理的,这应该是导致环境变量不同的主要原因;因为掌握的 linux 操作系统知识不多,所以只能猜测,还在深究原因中;
