最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關于Golang獲取當前項目絕對路徑的問題

 更新時間:2022年04月22日 10:36:24   作者:快樂的GTD吧  
這篇文章主要介紹了Golang獲取當前項目絕對路徑的問題,通常的做法是go run用于本地開發(fā),用一個命令中快速測試代碼確實非常方便;在部署生產環(huán)境時,我們會通過go build構建出二進制文件然后上傳到服務器再去執(zhí)行,那么會產生什么問題呢?感興趣的朋友一起看看吧

導讀

由于Golang是編譯型語言(非腳本型語言),如果你想在Golang程序中獲取當前執(zhí)行目錄將是一件非常蛋疼的事情。以前大家最折中的解決方案就是通過啟動傳參或是環(huán)境變量將路徑手動傳遞到程序,而今天我在看日志庫的時候發(fā)現(xiàn)了一種新的解決方案。
Go程序兩種不同的執(zhí)行方式

用Go編寫的程序有兩種執(zhí)行方式,go run和go build

通常的做法是go run用于本地開發(fā),用一個命令中快速測試代碼確實非常方便;在部署生產環(huán)境時,我們會通過go build構建出二進制文件然后上傳到服務器再去執(zhí)行。

兩種啟動方式會產生什么問題?

那么兩種啟動方式下,獲取到當前執(zhí)行路徑會產生什么問題?

話不多說,我們直接上代碼

我們編寫獲取當前可執(zhí)行文件路徑的方法

package main

import (
"fmt"
"log"
"os"
"path/filepath"
)
func main() {
fmt.Println("getCurrentAbPathByExecutable = ", getCurrentAbPathByExecutable())
}
// 獲取當前執(zhí)行程序所在的絕對路徑
func getCurrentAbPathByExecutable() string {
exePath, err := os.Executable()
if err != nil {
log.Fatal(err)
res, _ := filepath.EvalSymlinks(filepath.Dir(exePath))
return res

首先通過go run啟動

D:\Projects\demo>go run main.go
getCurrentAbPathByExecutable = C:\Users\XXX\AppData\Local\Temp\go-build216571510\b001\exe

再嘗試go build執(zhí)行

D:\Projects\demo>go build & demo.exe
getCurrentAbPathByExecutable = D:\Projects\demo

通過對比執(zhí)行結果,我們發(fā)現(xiàn)兩種執(zhí)行方式,我們獲取到了不同的路徑。而且很明顯,go run獲取到的路徑是錯誤的。

原因: 這是由于go run會將源代碼編譯到系統(tǒng)TEMP或TMP環(huán)境變量目錄中并啟動執(zhí)行;而go build只會在當前目錄編譯出可執(zhí)行文件,并不會自動執(zhí)行。

我們可以簡單理解為,go run main.go等價于go build & ./main

雖然兩種執(zhí)行方式最終都是一樣的過程:源碼->編譯->可執(zhí)行文件->執(zhí)行輸出,但他們的執(zhí)行目錄卻完全不一樣了。
新的方案誕生

這是在我今天查看服務日志(zap庫)的時候,突然反應過來一件事情。比如下面是一條簡單的日志,而服務是通過go run啟動的,但日志庫卻把我正確的程序路徑D:/Projects/te-server/modules/es/es.go:139給打印出來了

2021-03-26 17:47:06 D:/Projects/te-server/modules/es/es.go:139 update es index {"index": "tags", "data": "[200 OK] {"acknowledged":true}"}

于是我馬上去翻看zap源碼,發(fā)現(xiàn)是通過runtime.Caller()實現(xiàn)的,其實所有Golang日志庫都會有runtime.Caller()這個調用。

我開心的以為找到了最終答案,然后寫代碼試了下:

package main

import (
"fmt"
"path"
"runtime"
)
func main() {
fmt.Println("getCurrentAbPathByCaller = ", getCurrentAbPathByCaller())
}
// 獲取當前執(zhí)行文件絕對路徑(go run)
func getCurrentAbPathByCaller() string {
var abPath string
_, filename, _, ok := runtime.Caller(0)
if ok {
abPath = path.Dir(filename)
return abPath

首先在windows下面go run 和go build試一下

D:\Projects\demo>go run main.go
getCurrentAbPathByCaller = D:/Projects/demo

D:\Projects\demo>go build & demo.exe
getCurrentAbPathByCaller = D:/Projects/demo

嗯~~ 結果完全正確!

然后我再把構建好的程序扔到linux再運行后,它把我windows的路徑給打印出來了 --!

[root@server app]# chmod +x demo
[root@server app]# ./demo
getCurrentAbPathByCaller = D:/Projects/demo

沒想到白白高興一場,這個時候我就在想,既然go run時可以通過runtime.Caller()獲取到正確的結果,go build時也可以通過 os.Executable()來獲取到正確的路徑;

那如果我能判定當前程序是通過go run還是go build執(zhí)行的,選擇不同的路徑獲取方法,所有問題不就迎刃而解了嗎。
區(qū)分程序是go run還是go build執(zhí)行

Go沒有提供接口讓我們區(qū)分程序是go run還是go build執(zhí)行,但我們可以換個思路來實現(xiàn):

根據(jù)go run的執(zhí)行原理,我們得知它會源代碼編譯到系統(tǒng)TEMP或TMP環(huán)境變量目錄中并啟動執(zhí)行;

那我們可以直接在程序中對比os.Executable()獲取到的路徑是否與環(huán)境變量TEMP設置的路徑相同, 如果相同,說明是通過go run啟動的,因為當前執(zhí)行路徑是在TEMP目錄;不同的話自然是go build的啟動方式。

下面是完整代碼:

package main

import (
"fmt"
"log"
"os"
"path"
"path/filepath"
"runtime"
"strings"
)
func main() {
fmt.Println("getTmpDir(當前系統(tǒng)臨時目錄) = ", getTmpDir())
fmt.Println("getCurrentAbPathByExecutable(僅支持go build) = ", getCurrentAbPathByExecutable())
fmt.Println("getCurrentAbPathByCaller(僅支持go run) = ", getCurrentAbPathByCaller())
fmt.Println("getCurrentAbPath(最終方案-全兼容) = ", getCurrentAbPath())
}
// 最終方案-全兼容
func getCurrentAbPath() string {
dir := getCurrentAbPathByExecutable()
if strings.Contains(dir,getTmpDir()) {
return getCurrentAbPathByCaller()
return dir
// 獲取系統(tǒng)臨時目錄,兼容go run
func getTmpDir() string {
dir := os.Getenv("TEMP")
if dir == "" {
dir = os.Getenv("TMP")
res, _ := filepath.EvalSymlinks(dir)
return res
// 獲取當前執(zhí)行文件絕對路徑
func getCurrentAbPathByExecutable() string {
exePath, err := os.Executable()
if err != nil {
log.Fatal(err)
res, _ := filepath.EvalSymlinks(filepath.Dir(exePath))
// 獲取當前執(zhí)行文件絕對路徑(go run)
func getCurrentAbPathByCaller() string {
var abPath string
_, filename, _, ok := runtime.Caller(0)
if ok {
abPath = path.Dir(filename)
return abPath

在windows執(zhí)行

D:\Projects\demo>go run main.go
getTmpDir(當前系統(tǒng)臨時目錄) = C:\Users\XXX\AppData\Local\Temp
getCurrentAbPathByExecutable(僅支持go build) = C:\Users\XXX\AppData\Local\Temp\go-build456189690\b001\exe
getCurrentAbPathByCaller(僅支持go run) = D:/Projects/demo
getCurrentAbPath(最終方案-全兼容) = D:/Projects/demo
D:\Projects\demo>go build & demo.exe
getTmpDir(當前系統(tǒng)臨時目錄) = C:\Users\XXX\AppData\Local\Temp
getCurrentAbPathByExecutable(僅支持go build) = D:\Projects\demo
getCurrentAbPathByCaller(僅支持go run) = D:/Projects/demo
getCurrentAbPath(最終方案-全兼容) = D:\Projects\demo

在windows編譯后上傳到Linux執(zhí)行

[root@server app]# pwd
/data/app
[root@server app]# ./demo
getTmpDir(當前系統(tǒng)臨時目錄) = .
getCurrentAbPathByExecutable(僅支持go build) = /data/app
getCurrentAbPathByCaller(僅支持go run) = D:/Projects/demo
getCurrentAbPath(最終方案-全兼容) = /data/app

對比結果,我們可以看到,在不同的系統(tǒng)中,不同的執(zhí)行方式,我們封裝的getCurrentAbPath方法最終都輸出的正確的結果,perfect!

到此這篇關于關于Golang獲取當前項目絕對路徑的問題的文章就介紹到這了,更多相關Golang絕對路徑內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Golang logrus 日志包及日志切割的實現(xiàn)

    Golang logrus 日志包及日志切割的實現(xiàn)

    這篇文章主要介紹了Golang logrus 日志包及日志切割的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-02-02
  • GO語言Defer用法實例分析

    GO語言Defer用法實例分析

    這篇文章主要介紹了GO語言Defer用法,實例分析了Defer的使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • Golang根據(jù)job數(shù)量動態(tài)控制每秒?yún)f(xié)程的最大創(chuàng)建數(shù)量方法詳解

    Golang根據(jù)job數(shù)量動態(tài)控制每秒?yún)f(xié)程的最大創(chuàng)建數(shù)量方法詳解

    這篇文章主要介紹了Golang根據(jù)job數(shù)量動態(tài)控制每秒?yún)f(xié)程的最大創(chuàng)建數(shù)量方法
    2024-01-01
  • golang逐行讀取文件的操作

    golang逐行讀取文件的操作

    這篇文章主要介紹了golang逐行讀取文件的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Golang 錯誤捕獲Panic與Recover的使用

    Golang 錯誤捕獲Panic與Recover的使用

    對于Go語言的錯誤是通過返回值的方式,本文主要介紹了Golang 錯誤捕獲Panic與Recover的使用,文中根據(jù)實例編碼詳細介紹的十分詳盡,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • go實現(xiàn)圖片拼接與文字書寫的方法實例

    go實現(xiàn)圖片拼接與文字書寫的方法實例

    這篇文章主要給大家介紹了關于go實現(xiàn)圖片拼接與文字書寫的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2022-01-01
  • Go語言中的格式化占位符的實現(xiàn)示例

    Go語言中的格式化占位符的實現(xiàn)示例

    在Go語言中,fmt包提供了豐富的格式化占位符用于輸出不同類型的數(shù)據(jù),了解和選擇合適的占位符對于確保輸出內容的正確性和可讀性至關重要,本文就來介紹一下,感興趣的可以學習
    2024-10-10
  • Golang學習筆記(三):控制流

    Golang學習筆記(三):控制流

    這篇文章主要介紹了Golang學習筆記(三):控制流,本文講解了IF、FOR、SWITCH、goto、break、continue等控制流語句的使用實例,需要的朋友可以參考下
    2015-05-05
  • 簡單聊聊為什么說Go語言字符串是不可變的

    簡單聊聊為什么說Go語言字符串是不可變的

    最近有讀者留言說,平時在寫代碼的過程中,是會對字符串進行修改的,但網(wǎng)上都說 Go 語言字符串是不可變的,這是為什么呢,本文就來和大家簡單講講
    2023-05-05
  • golang croncli 定時器命令詳解

    golang croncli 定時器命令詳解

    定時器是執(zhí)行任務時的常用功能,配置系統(tǒng)的定時任務太麻煩,所以就想用golang簡單實現(xiàn)一個定時器命令,包括定時器命令格式、定時執(zhí)行命令的相關知識,感興趣的朋友跟隨小編一起看看吧
    2022-03-03

最新評論

东源县| 虎林市| 丹棱县| 离岛区| 天峨县| 类乌齐县| 邯郸市| 华亭县| 临朐县| 建湖县| 峨边| 侯马市| 松滋市| 平顺县| 泽州县| 青冈县| 金坛市| 遂川县| 康定县| 若尔盖县| 丽江市| 公主岭市| 四川省| 南丰县| 蕉岭县| 伊通| 眉山市| 龙游县| 石林| 通辽市| 正安县| 姜堰市| 富源县| 剑阁县| 马公市| 福泉市| 远安县| 丘北县| 三都| 广宗县| 资中县|