วิธีรับไดเรกทอรีของไฟล์ที่กำลังทำงานอยู่


239

ใน nodejs ผมใช้__dirname อะไรคือสิ่งที่เท่าเทียมกันใน Golang?

ฉันมี googled และพบบทความนี้http://andrewbrookins.com/tech/golang-get-directory-of-the-current-file/ ที่ซึ่งเขาใช้รหัสด้านล่าง

_, filename, _, _ := runtime.Caller(1)
f, err := os.Open(path.Join(path.Dir(filename), "data.csv"))

แต่มันเป็นวิธีที่ถูกต้องหรือเป็นวิธีสำนวนที่ต้องทำใน Golang?


นี่ไม่ใช่คำตอบสำหรับคำถามของคุณ แต่คุณอาจแคชพา ธ ไปยัง var ส่วนกลาง (ตำแหน่งไฟล์ของคุณไม่สามารถเปลี่ยนแปลงได้ในขณะที่ทำงาน :)) ไม่ให้เรียกใช้ os.open ซ้ำแล้วซ้ำอีกทุกครั้งที่โค้ดของคุณทำงาน
oguzalb

คุณควรจะผ่าน0ไม่ได้ไป1 runtime.Caller()
fiatjaf

4
runtime.Caller(0)$GOPATH/src/packagename/main.goจะให้เส้นทางของแฟ้มแหล่งที่มาเช่น คำตอบอื่น ๆ ในชุดข้อความนี้กำลังพยายามส่งคืนพา ธ ของไฟล์เรียกทำงาน (เช่น$GOPATH/bin/packagename)
fiatjaf

คุณกำลังสมมติว่าโปรแกรมจะวิ่งออกมาจากไฟล์ ...
Flimzy

1
ซ้ำซ้อนที่เป็นไปได้ของ: ค้นหาเส้นทางไปยังปฏิบัติการ
Jocelyn

คำตอบ:


221

สิ่งนี้ควรทำ:

import (
    "fmt"
    "log"
    "os"
    "path/filepath"
)

func main() {
    dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
    if err != nil {
            log.Fatal(err)
    }
    fmt.Println(dir)
}

2
เป็นไปได้ไหมที่จะมีข้อผิดพลาดที่นี่? ถ้าเป็นเช่นนั้นสิ่งที่ผิดพลาดจะออกมาจากความอยากรู้อยากเห็น?
Jeff Escalante

4
ไม่ทำงานสำหรับฉันplay.golang.org/p/c8fe-Zm_bH - os.Args [0] ไม่จำเป็นต้องมีเส้นทาง abs
zupa

5
ใช้งานได้จริงแม้ว่า os.Args [0] ไม่มีเส้นทาง abs เหตุผลที่ทำให้สนามเด็กเล่นไม่ใช่สิ่งที่คุณคาดหวังเพราะมันอยู่ในกล่องทราย
Gustavo Niemeyer

37
นี่ไม่ใช่วิธีที่เชื่อถือได้ดูคำตอบเกี่ยวกับการใช้osextเนื่องจากเป็นการใช้งานที่ทำงานร่วมกับลูกค้าของเราทั้งหมดในระบบปฏิบัติการต่าง ๆ ฉันใช้รหัสโดยใช้วิธีการนี้ แต่ดูเหมือนว่าจะไม่น่าเชื่อถือมากและผู้ใช้หลายคนบ่นว่าบั๊กที่เกิดจากวิธีนี้เลือกเส้นทางที่ไม่ถูกต้องสำหรับปฏิบัติการ
JD D

5
ได้ผลลัพธ์เช่นเดียวกับ emrah โดยใช้ Go 1.6 บน Windows (มีพา ธ ของโฟลเดอร์ temp แทนที่จะเป็นโฟลเดอร์ไฟล์ต้นฉบับ) ในการรับพา ธ ของโฟลเดอร์ซอร์สไฟล์ของคุณโดยไม่ต้องพึ่งพาภายนอกให้ใช้โค้ด OP ของเวอร์ชันที่แก้ไข_, currentFilePath, _, _ := runtime.Caller(0) dirpath := path.Dir(currentFilePath)runtime.Caller(0)runtime.Caller(1)
เล็กน้อย

295

แก้ไข: ตั้งแต่วันที่ 1.8 (ปล่อยตัวกุมภาพันธ์ 2560) วิธีที่แนะนำในการทำเช่นนี้คือos.Executable:

func Executable() (string, error)

Executable ส่งคืนชื่อพา ธ สำหรับ executable ที่เริ่มต้นกระบวนการปัจจุบัน ไม่มีการรับประกันว่าเส้นทางจะยังคงชี้ไปที่ปฏิบัติการที่ถูกต้อง หากมีการใช้ symlink เพื่อเริ่มกระบวนการขึ้นอยู่กับระบบปฏิบัติการผลลัพธ์อาจเป็น symlink หรือพา ธ ที่ชี้ไป หากต้องการผลลัพธ์ที่มั่นคงเส้นทาง / filepath.EvalSymlinks อาจช่วยได้

เพื่อให้ได้ไดเรกทอรีของแฟ้มโปรแกรมที่คุณสามารถpath/filepath.Dirใช้ได้

ตัวอย่าง :

package main

import (
    "fmt"
    "os"
    "path/filepath"
)

func main() {
    ex, err := os.Executable()
    if err != nil {
        panic(err)
    }
    exPath := filepath.Dir(ex)
    fmt.Println(exPath)
}

คำตอบเดิม:

คุณควรจะสามารถใช้ os.Getwd

func Getwd() (pwd string, err error)

Getwd ส่งคืนชื่อพา ธ ที่ถูกรูทที่สอดคล้องกับไดเรกทอรีปัจจุบัน หากไดเรกทอรีปัจจุบันสามารถเข้าถึงได้ผ่านหลายเส้นทาง (เนื่องจากลิงก์สัญลักษณ์) Getwd อาจส่งคืนหนึ่งในไดเรกทอรีเหล่านั้น

ตัวอย่างเช่น:

package main

import (
    "fmt"
    "os"
)

func main() {
    pwd, err := os.Getwd()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    fmt.Println(pwd)
}

3
นี่คือไดเรกทอรีการทำงานของกระบวนการปัจจุบัน ใน nodejs เทียบเท่ากับ process.cwd () nodejs.org/api/process.html#process_process_cwd
ekanna

2
ตกลงฉันเห็นความแตกต่าง จากคุณหลังจากตำแหน่งของไบนารีใน filesytem (แทนที่จะเป็นไดเรกทอรีการทำงานปัจจุบัน) ฉันคิดว่าruntime.Callerมันใกล้เคียงที่สุดที่คุณจะได้รับ "สำนวน"
Intermernet

3
'เปิดตัวกุมภาพันธ์ 2017'? ดูเหมือนว่าจะมีการประดิษฐ์เครื่องจับเวลาและเรามีสมาชิกโพสต์จากอนาคต เป็นการดีที่จะทราบว่าเวอร์ชันในอนาคตจะมีวิธีการข้ามแพลตฟอร์มที่เชื่อถือได้ในขณะเดียวกันเราต้องติดกับโซลูชั่นที่มีอยู่ในปัจจุบัน
ljgww

1
@ljgww ขออภัยฉันจะใช้ Delorean ของฉันและกลับบ้าน :-) ฉันอัปเดตคำตอบของฉันล่วงหน้าเพราะฉันเพิ่งเห็นว่าคุณสมบัติที่จะเกิดขึ้นและคิดว่าฉันลืมที่จะปรับปรุงคำตอบในภายหลัง
Intermernet

1
แก้ไขด้วยpath/filepath.Dirเพราะpath.Dirใช้งานได้กับเครื่องหมายสแลช (สไตล์ Unix) เป็นตัวคั่นไดเรกทอรีเท่านั้น
โจเซลีน

63

ใช้แพ็คเกจosext

มันมีฟังก์ชั่นExecutableFolder()ที่ส่งคืนพา ธ สัมบูรณ์ไปยังโฟลเดอร์ที่มีโปรแกรมที่รันอยู่ในปัจจุบัน (มีประโยชน์สำหรับงาน cron) มันเป็นแพลตฟอร์มข้าม

เอกสารออนไลน์

package main

import (
    "github.com/kardianos/osext"
    "fmt"
    "log"
)

func main() {
    folderPath, err := osext.ExecutableFolder()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(folderPath)
}

13
นี่เป็นคำตอบเดียวที่สร้างผลลัพธ์ที่คาดหวังสำหรับฉันทั้งบน Windows และบน Linux
DannyB

3
ใช้งานได้ดีจนกว่าคุณจะต้องการใช้กับgo run main.goการพัฒนาท้องถิ่น ไม่แน่ใจว่าจะได้รับสิ่งที่ดีที่สุดได้อย่างไรโดยไม่ต้องสร้างไฟล์ปฏิบัติการล่วงหน้าทุกครั้ง
Derek Dowling

1
go runขออภัยผมไม่ทราบวิธีการที่จะทำให้มันทำงานร่วมกับ ไบนารีนี้จะอยู่ในโฟลเดอร์ชั่วคราวทุกครั้ง
DobrosławŻybort

2
@DerekDowling วิธีที่จะเป็นครั้งแรกที่ทำแล้วทำงานgo install จะบอกคุณว่าไฟล์ที่ถูกสร้างขึ้น โดยทั่วไปฉันได้พบว่าแตกต่างกันระหว่างเวลาและเป็นที่ยอมรับถ้าผมเคยทำงานอยู่แล้ว สำหรับผู้ใช้ windows บน powershell คำสั่งจะเป็นgo build -v *.go && ./main-vgo rungo buildgo installgo build -v {*}.go && ./main.exe
kumarharsh

ตั้งแต่นี้จะกลับมา$GOPATH/bin/ทำไมไม่ใช้$GOPATH/bin/?
fiatjaf

10
filepath.Abs("./")

Abs ส่งคืนการแทนค่าเส้นทางแบบสัมบูรณ์ หากเส้นทางไม่สมบูรณ์จะถูกรวมเข้ากับไดเรกทอรีการทำงานปัจจุบันเพื่อเปลี่ยนเป็นเส้นทางที่แน่นอน

ตามที่ระบุไว้ในความคิดเห็นนี้จะส่งคืนไดเรกทอรีที่ใช้งานอยู่ในปัจจุบัน


8
สิ่งนี้จะส่งคืนไดเร็กทอรีปัจจุบันไม่ใช่ไดเร็กทอรีของไฟล์ปัจจุบัน เช่นนี้จะแตกต่างกันถ้าปฏิบัติการถูกเรียกจากเส้นทางที่แตกต่างกัน
Fujii

8

ถ้าคุณใช้วิธีนี้:

dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
    log.Fatal(err)
}
fmt.Println(dir)

คุณจะได้รับพา ธ / tmp เมื่อคุณใช้งานโปรแกรมโดยใช้ IDE บางตัวเช่น GoLang เนื่องจากไฟล์ที่เรียกทำงานจะบันทึกและรันจาก / tmp

ฉันคิดว่าวิธีที่ดีที่สุดในการรับไดเรกทอรีงานปัจจุบันหรือ '.' คือ :

import(
  "os" 
  "fmt"
  "log"
)

func main() {
  dir, err := os.Getwd()
    if err != nil {
        log.Fatal(err)
    }
  fmt.Println(dir)
}

os.Getwd ()ฟังก์ชั่นจะกลับไดเรกทอรีการทำงานปัจจุบัน และทั้งหมดโดยไม่ต้องใช้ไลบรารีภายนอก: D


สิ่งนี้ไม่ถูกต้อง สิ่งนี้จะส่งคืนไดเร็กทอรีการทำงานของผู้ใช้ที่ดำเนินการกระบวนการและไม่ใช่ไดเร็กทอรีของไฟล์ ใช้ filepath.abs
PodTech.io

1
จะส่งคืน Directory ที่ใช้งานของไฟล์เรียกทำงานที่รันอยู่ ถ้าคุณใช้ IDE เช่น goland และไม่มีการกำหนดค่าไดเรกทอรีทำงานในตัวเลือกการสร้างมันจะทำงานจาก / tmp ดังนั้นการใช้งาน / tmp สำหรับคุณ!? แต่ถ้าคุณใช้ os.Getwd () มัน ส่งคืนพา ธ ไฟล์ที่รันได้. exe หรือ elf ไม่ใช่ / tmp
Bit

@Bit การใช้แม่แบบพื้นฐานของการดีบักใน IDE นั้นให้คุณแล้วกด 'แก้ไขการกำหนดค่า' และกรอก 'ไดเรกทอรีไดเรกทอรี' ดังนั้นคุณจะเห็นเส้นทาง 'os.Args [0]' เป็นสิ่งที่คุณต้องการ
ϻαϻɾΣɀО -MaMrEzO

5

ถ้าคุณใช้แพ็คเกจosextโดยkardianosและคุณจำเป็นต้องทดสอบภายในเครื่องเช่น Derek Dowling แสดงความคิดเห็น:

ใช้งานได้ดีจนกระทั่งคุณต้องการใช้กับ go run main.go เพื่อการพัฒนาท้องถิ่น ไม่แน่ใจว่าจะได้รับสิ่งที่ดีที่สุดได้อย่างไรโดยไม่ต้องสร้างไฟล์ปฏิบัติการล่วงหน้าทุกครั้ง

วิธีแก้ปัญหานี้คือการสร้างยูทิลิตี gorun.exe แทนที่จะใช้ go run ยูทิลิตี gorun.exe จะรวบรวมโครงการโดยใช้ "go build" จากนั้นเรียกใช้ทันทีหลังจากนั้นในไดเรกทอรีปกติของโครงการของคุณ

ฉันมีปัญหากับคอมไพเลอร์อื่น ๆ และพบว่าตัวเองกำลังสร้างยูทิลิตี้เหล่านี้เพราะพวกเขาไม่ได้ส่งมาพร้อมคอมไพเลอร์ ... มันเป็นความลับโดยเฉพาะอย่างยิ่งกับเครื่องมือเช่น C ที่คุณต้องรวบรวมและเชื่อมโยงแล้วเรียกใช้

หากใครชอบแนวคิดของฉันของ gorun.exe (หรือเอลฟ์) ฉันจะอัปโหลดไปยัง gitub เร็ว ๆ นี้ ..

ขออภัยคำตอบนี้มีความหมายว่าเป็นความคิดเห็น แต่ฉันไม่สามารถแสดงความคิดเห็นได้เนื่องจากฉันยังไม่มีชื่อเสียงมากพอ

อีกวิธีหนึ่งคือ "go run" สามารถแก้ไขได้ (หากไม่มีคุณสมบัตินี้อยู่แล้ว) เพื่อให้มีพารามิเตอร์เช่น "go run -notemp" เพื่อไม่ให้เรียกใช้โปรแกรมในไดเรกทอรีชั่วคราว (หรือสิ่งที่คล้ายกัน) แต่ฉันต้องการพิมพ์ gorun หรือ "gor" เพราะสั้นกว่าพารามิเตอร์ที่ซับซ้อน ต้องติดตั้ง Gorun.exe หรือ gor.exe ในไดเรกทอรีเดียวกับคอมไพเลอร์ go ของคุณ

การใช้ gorun.exe (หรือ gor.exe) จะไม่สำคัญอย่างที่ฉันทำกับคอมไพเลอร์อื่น ๆ ในโค้ดเพียงไม่กี่บรรทัด ... (คำสุดท้ายที่มีชื่อเสียง ;-)


3
หากคุณต้องการให้ทั้งสองทำงานกับ "go run" และสร้าง executable ได้ให้ใช้_, callerFile, _, _ := runtime.Caller(0) executablePath := filepath.Dir(callerFile)แทน
Jocelyn

@ โจเซลีนความคิดเห็นของคุณยอดเยี่ยมมากจนคุณควรตอบคำถามนี้เต็ม! นี่เป็นเคล็ดลับสำหรับฉัน - ในการตั้งค่าของฉันเองฉันมีสำเนาของสภาพแวดล้อมใน macOS ซึ่งส่วนใหญ่ฉันใช้เพื่อตรวจจับข้อผิดพลาดทางไวยากรณ์ (และข้อผิดพลาดเล็กน้อย) จากนั้นฉันซิงค์รหัสไปยังเซิร์ฟเวอร์การปรับใช้ซึ่งทำงานภายใต้ Ubuntu Linux และแน่นอนว่าสภาพแวดล้อมนั้นแตกต่างอย่างสิ้นเชิง ... ดังนั้นจึงมีความต้องการที่แท้จริงในการคิดว่าเส้นทางของไฟล์นั้นโหลดเทมเพลตไฟล์การตั้งค่าคงที่หรือไม่ ไฟล์ ฯลฯ ...
Gwyneth Llewelyn

4

os.Executable: https://tip.golang.org/pkg/os/#Executable

filepath.EvalSymlinks: https://golang.org/pkg/path/filepath/#EvalSymlinks

การสาธิตเต็มรูปแบบ:

package main

import (
    "fmt"
    "os"
    "path/filepath"
)

func main() {
    var dirAbsPath string
    ex, err := os.Executable()
    if err == nil {
        dirAbsPath = filepath.Dir(ex)
        fmt.Println(dirAbsPath)
        return
    }

    exReal, err := filepath.EvalSymlinks(ex)
    if err != nil {
        panic(err)
    }
    dirAbsPath = filepath.Dir(exReal)
    fmt.Println(dirAbsPath)
}

4

บางครั้งก็เพียงพอแล้วอาร์กิวเมนต์แรกจะเป็นเส้นทางของไฟล์เสมอ

package main

import (
    "fmt"
    "os"
)


func main() {
    fmt.Println(os.Args[0])

    // or
    dir, _ := os.Getwd()
    fmt.Println(dir)
}

0

คำตอบของ Gustavo Niemeyer นั้นยอดเยี่ยม แต่ใน Windows รันไทม์ proc ส่วนใหญ่อยู่ใน dir อื่นเช่นนี้:

"C:\Users\XXX\AppData\Local\Temp"

หากคุณใช้พา ธ ไฟล์แบบสัมพัทธ์"/config/api.yaml"จะใช้พา ธ โปรเจ็กต์ของคุณที่มีรหัสของคุณอยู่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.