如何在不同环境中找到兄弟文件,当 os.Getwd() 返回的路径不同时。

huangapple go评论88阅读模式
英文:

How to find a sibling file when the os.Getwd() is different in different environments

问题

myprogram/
 |
 |-main.go
 |-dir1/
     |-data/
         |-datafile.json
     |-runner.go
     |-runner_test.go

runner.go 中,我有一个简单的函数用于读取 datafile.json。类似这样:

   func GetPayload() (string, err) {
    		dBytes, dErr := ioutil.ReadFile("dir1/data/datafile.json")
            if dErr != nil { return nil, dErr}
            return dBytes, nil
   }

我在一个类似上面的结构中使用 Go 语言在 Lambda 中。当 Lambda 在其实际环境中运行时,它从 main.go 开始,然后调用 runner.go 中的 GetPayload()。然而,在 runner_test.go 中,我有一个在简单的工作节点机器上运行的测试,也会调用 GetPayload()

在“正常”执行(从 main.go)中,这个工作正常。然而,当从 runner_test.go 调用 GetPayload() 时,它会报错,显示:

打开 dir1/data/datafile.json:没有那个文件或目录

这是有道理的,因为在测试期间,工作目录是包含 runner_test.go 的目录,即 data/,所以它没有 dir1 作为其子目录。我一直在尝试使用 os.Getwd() 并从那里获取路径,例如:

   pwd, _ := os.Getwd()
   dBytes, dErr := ioutil.ReadFile(pwd + "dir1/data/datafile.json")

但是,这样也行不通,因为对于 runner_test.go,pwd 是 user/myname/myprogram/dir1,但对于 main.go,它变成了 user/myname/myprogram

有什么办法可以在任何环境中从 GetPayload() 中找到并打开 datafile.json 吗?我可以向 GetPayload() 传递一个可选参数,但如果可能的话,最好避免这样做。


<details>
<summary>英文:</summary>

myprogram/
|
|-main.go
|-dir1/
|-data/
|-datafile.json
|-runner.go
|-runner_test.go

In `runner.go`, I have a simple function that reads the `datafile.json`. Something like

```golang
   func GetPayload() (string, err) {
    		dBytes, dErr := ioutil.ReadFile(&quot;dir1/data/datafile.json&quot;)
            if dErr != nil { return nil, dErr}
            return dBytes, nil
   }

I'm using Go in a Lambda with a structure similar to above. When the Lambda runs in its actual environment, it starts at main.go, and then invokes GetPayload() from runner.go. However, I have a test in a simple worker node machine in runner_test.go that also hits GetPayload() .

During "normal" execution (from main.go) - this works OK. However, when GetPayload() is invoked from runner_test.go, it errors, saying

> open dir1/data/datafile.json no such file or directory

This makes sense, because during the test, the working directory is the directory that houses runner_test.go, which is data/, so there is no dir1 as a child of it. I've been trying to play with using os.Getwd() and getting the paths from there like:

   pwd, _ := os.Getwd()
   dBytes, dErr := ioutil.ReadFile(pwd + &quot;dir1/data/datafile.json&quot;)

But again, that won't work, because for runner_test.go pwd is user/myname/myprogram/dir1, but from main.go, it turns up as user/myname/myprogram.

Any idea how I can find and open datafile.json from within GetPayload() in any environment? I could pass an optional parameter to GetPayload() but if possible, it'd be great to avoid that.

答案1

得分: 3

如果文件是静态的(即在构建程序后不需要更改),你可以将其嵌入到构建的程序中。这意味着你不再需要担心运行时文件路径的问题。

现在,你的data/目录中的文件被嵌入到名为dataFiles的变量中,该变量充当只读文件系统。

更多信息:

包文档概述中阅读有关嵌入的更多信息。

阅读我关于“何时使用嵌入”的回答2

英文:

If the file is static (meaning that it doesn't need to change after you build the program), you can embed it into the built program. This means you no longer have to worry about run-time file paths.

import (
    &quot;embed&quot;
)

//go:embed data/*
var dataFiles embed.FS

func GetPayload() (string, err) {
    dBytes, dErr := dataFiles.ReadFile(dataFiles, &quot;data/datafile.json&quot;)
    if dErr != nil { return nil, dErr}
    return dBytes, nil
}

Now the files in your data/ directory are embedded in this variable dataFiles which acts as a read-only file system.

For more info:

Read more about embed in the package documentation overview.

Read my answer about "when to use embed"

答案2

得分: 0

对于程序在运行时需要的数据文件,可以使用固定的目录并引用该目录,或者接受命令行参数或某种配置来告诉你文件的位置。

在运行单元测试时,wd(工作目录)是包含测试文件的目录。一种约定是在包含测试的目录下使用testdata/目录,并将所有数据文件放在那里。这样,你可以通过使用testdata/datafile.json来从测试中引用该文件。

你可以使用运行时需要的文件的副本作为测试文件,或者可以使用testdata/目录下的测试文件到运行时数据文件的符号链接。

英文:

For data files that your program needs during runtime, either use a fixed directory and refer to that, or accept a command line argument or some sort of configuration that tells you where the file is.

When running unit tests, the wd is the directory containing the test file. One convention is to use a testdata/ directory under the directory containing the test, and put all data files there. That way you can refer to that file from the test by using testdata/datafile.json.

You can use a copy of the file you need during runtime as your test file, or you can use a symlink from the runtime data file to the test file under the testdata/ dir.

答案3

得分: 0

对于程序在运行时需要的数据文件,可以使用一个固定的目录并引用该目录。以下是一个示例代码:

package main

import (
   "os"
   "path/filepath"
)

func main() {
   d, err := os.UserCacheDir()
   if err != nil {
      panic(err)
   }
   d = filepath.Join(d, "file.json")
   f, err := os.Open(d)
   if err != nil {
      panic(err)
   }
   defer f.Close()
   os.Stdout.ReadFrom(f)
}

你可以使用os.UserCacheDir()函数获取用户缓存目录的路径,然后使用filepath.Join()函数将文件名拼接到路径上。接下来,使用os.Open()函数打开文件,并在处理完文件后使用defer语句关闭文件。最后,使用os.Stdout.ReadFrom()函数将文件内容输出到标准输出。

你可以参考以下链接获取更多关于os.UserCacheDir()os.UserConfigDir()的信息:

英文:

> For data files that your program needs during runtime, either use a fixed
> directory and refer to that

Someone made this suggestion, which I agree with. To that end, you can use
something like this:

package main

import (
   &quot;os&quot;
   &quot;path/filepath&quot;
)

func main() {
   d, err := os.UserCacheDir()
   if err != nil {
      panic(err)
   }
   d = filepath.Join(d, &quot;file.json&quot;)
   f, err := os.Open(d)
   if err != nil {
      panic(err)
   }
   defer f.Close()
   os.Stdout.ReadFrom(f)
}

huangapple
  • 本文由 发表于 2021年6月19日 04:07:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/68040901.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定