<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      轉載自:https://mp.weixin.qq.com/s/5biKspJu8Hmxsert2liUtg

      導讀

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

      Go程序兩種不同的執行方式

      用Go編寫的程序有兩種執行方式,go rungo build

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

       

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

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

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

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

      package main
      
      import (
          "fmt"
          "log"
          "os"
          "path/filepath"
      )
      
      func main() {
          fmt.Println("getCurrentAbPathByExecutable = ", getCurrentAbPathByExecutable())
      }
      
      
      // 獲取當前執行程序所在的絕對路徑
      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執行

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

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

      原因: 這是由于go run會將源代碼編譯到系統TEMPTMP環境變量目錄中并啟動執行;而go build只會在當前目錄編譯出可執行文件,并不會自動執行。

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

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

      新的方案誕生

      這是在我今天查看服務日志(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源碼,發現是通過runtime.Caller()實現的,其實所有Golang日志庫都會有runtime.Caller()這個調用。

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

      package main
      
      import (
          "fmt"
          "path"
          "runtime"
      )
      
      func main() {
          fmt.Println("getCurrentAbPathByCaller = ", getCurrentAbPathByCaller())
      
      }
      
      // 獲取當前執行文件絕對路徑(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執行的,選擇不同的路徑獲取方法,所有問題不就迎刃而解了嗎。

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

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

      區分程序是go run還是go build執行

      Go沒有提供接口讓我們區分程序是go run還是go build執行,但我們可以換個思路來實現:

      根據go run的執行原理,我們得知它會源代碼編譯到系統TEMPTMP環境變量目錄中并啟動執行;

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

      下面是完整代碼:

      package main
      
      import (
          "fmt"
          "log"
          "os"
          "path"
          "path/filepath"
          "runtime"
          "strings"
      )
      
      func main() {
          fmt.Println("getTmpDir(當前系統臨時目錄) = ", 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
      }
      
      // 獲取系統臨時目錄,兼容go run
      func getTmpDir() string {
          dir := os.Getenv("TEMP")
          if dir == "" {
              dir = os.Getenv("TMP")
          }
          res, _ := filepath.EvalSymlinks(dir)
          return res
      }
      
      // 獲取當前執行文件絕對路徑
      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
      }

      在windows執行

      D:\Projects\demo>go run main.go
      getTmpDir(當前系統臨時目錄) =  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(當前系統臨時目錄) =  C:\Users\XXX\AppData\Local\Temp
      getCurrentAbPathByExecutable(僅支持go build) =  D:\Projects\demo
      getCurrentAbPathByCaller(僅支持go run) =  D:/Projects/demo
      getCurrentAbPath(最終方案-全兼容) =  D:\Projects\demo

      在windows編譯后上傳到Linux執行

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

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

      題外:

      #打印當前項目絕對路徑
      fmt.Println(filepath.Abs("./"))
      
      #切換當前工作路徑
      _ = os.Chdir(getCurrentAbPath())
      
      #打印當前工作路徑
      str, _ := os.Getwd()
      fmt.Println(str)

       

      posted on 2023-03-16 16:42  聰神carry  閱讀(409)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 无码av最新无码av专区| 亚洲sm另类一区二区三区| 男人的天堂av一二三区| 天堂va蜜桃一区二区三区| 无码乱人伦一区二区亚洲一| 日韩精品一区二区在线看| av深夜免费在线观看| 国产一卡2卡三卡4卡免费网站| 久久精品女人天堂av免费观看| 亚洲欧美日韩国产四季一区二区三区 | 内射无套内射国产精品视频| 色综合久久久久综合体桃花网| 亚洲欧美日韩高清一区二区三区| 爽爽精品dvd蜜桃成熟时电影院| 国产午夜精品视频在线播放| 久在线精品视频线观看| 奇米777四色在线精品| av色蜜桃一区二区三区| 国内精品国产三级国产a久久| 日韩精品中文字幕人妻| 精品国产一区二区三区av色诱 | 日韩熟妇中文色在线视频 | 久久亚洲女同第一区综合| 亚洲国产大片永久免费看| 亚洲鸥美日韩精品久久| 国产成人a在线观看视频免费| 日本高清在线观看WWW色| 特黄做受又粗又大又硬老头| 欧美韩中文精品有码视频在线 | 鲁丝一区鲁丝二区鲁丝三区| 激情国产一区二区三区四区| 天天爽夜夜爽人人爽曰| 亚洲精品国产av一区二区 | 亚洲午夜av久久久精品影院| 亚洲中文字幕无码久久2020| 国产精品久久毛片av大全日韩| 韩国深夜福利视频在线观看| 一本精品99久久精品77| 亚洲男女羞羞无遮挡久久丫 | 精品一区二区成人码动漫| 中文字幕人妻精品在线|