Golang获取当前项目路径 | 青训营笔记

681 阅读2分钟

前言

青训营抖音项目投稿视频需要在服务端生成封面文件,中间需要将文件暂存,为保证程序的健壮性,需要在不同环境下都可以稳定运行,而不同环境下的文件路径不同,主要是windows盘符路径与Linux不同,所以需要保证兼容。

两种启动方式带来的问题

用Go编写的程序有两种执行方式,go rungo build,通常的做法是go run用于本地开发,用一个命令中快速测试代码确实非常方便;在部署生产环境时,我们会通过go build构建出二进制文件然后上传到服务器再去执行。

但是,如果我们使用os.Executable()直接获取路径则会出现问题。

由于go run会将源代码编译到系统TEMPTMP环境变量目录中并启动执行;而go build只会在当前目录编译出可执行文件,并不会自动执行。go run main.go等价于go build & ./main,此时go run获取到的路径是错误的。

查阅资料,runtime.Caller()可以正确的获取不同启动方式的路径,但是如果在Windiws构建好项目,使用Linux启动项目则又会出现问题:Linux中启动会打印Windows下的路径。

解决方案

go run可以通过runtime.Caller()获取到正确的结果 go build可以通过 os.Executable()来获取到正确的路径

我们可以在程序中对比os.Executable()获取到的路径是否与环境变量TEMP设置的路径相同, 如果相同,说明是通过go run启动的,因为当前执行路径是在TEMP目录;不同的话则是go build的启动方式。

最终代码如下:

func GetCurrentAbPath() string {
	dir := getCurrentAbPathByExecutable()
	tmpDir, _ := filepath.EvalSymlinks(os.TempDir())
	if strings.Contains(dir, tmpDir) {
		return getCurrentAbPathByCaller()
	}
	return dir
}

// 获取当前执行文件绝对路径
func getCurrentAbPathByExecutable() string {
	exePath, err := os.Executable()
	if err != nil {
		log.Fatal(err)
	}
	res, _ := filepath.EvalSymlinks(filepath.Dir(exePath))
	return res
}

// 获取当前执行文件绝对路径(go run)
func getCurrentAbPathByCaller() string {
	var abPath string
	_, filename, _, ok := runtime.Caller(0)
	if ok {
		abPath = path.Dir(filename)
	}
	return abPath
}