英文:
golang os.Create cause "no such file or directory" error
问题
一定是有什么简单的问题,但我似乎无法弄清楚。我一直收到“没有这样的文件或目录”错误。我以为Create函数是用来创建新文件的?
package main
import (
    "log"
	"os"
)
func main() {
	f, err := os.Create("~/golang-server.log")
	defer f.Close()
	if err != nil {
	    panic(err.Error())
	}
	log.SetOutput(f)
}
英文:
Must be something simple, but I cannot seem to figure out. I keep getting "no such file or directory" error. Thought the Create function is to create a new file?
package main
import (
    "log"
	"os"
)
func main() {
	f, err := os.Create("~/golang-server.log")
	defer f.Close()
	if err != nil {
	    panic(err.Error())
	}
	log.SetOutput(f)
}
</details>
# 答案1
**得分**: 5
你不能使用`~`或环境变量(如`$HOME`)来指定文件路径,它们是字符串字面量,表示实际路径。你得到的错误是因为它将`~/golang-server.log`视为当前目录的相对路径,而当前目录中没有名为`~`的目录。
通过手动创建子目录`~`,你的代码将成功运行:
     ~/test/ mkdir \~
     ~/test/ go run t.go
     ~/test/ ls \~
    golang-server.log
因此,需要传递一个`绝对路径`或`相对路径`给`os.Create`。
<details>
<summary>英文:</summary>
You can't use `~` or environment variable like `$HOME` to specify the file path, they're string literal and means actual path. The error you got is because it treat `~/golang-server.log` as a relative path of current directory, and there's no directory `~` in current directory.
With manually created sub-directory `~`, your code will succeed:
     ~/test/ mkdir \~
     ~/test/ go run t.go
     ~/test/ ls \~
    golang-server.log
So need to pass an `absolute path` or `relative path` to `os.Create`.
</details>
# 答案2
**得分**: 4
你可以使用`os.ExpandEnv`来处理在sh/bash上下文中才有意义的"~"符号。
```go
os.Create(os.ExpandEnv("$HOME/golang-server.log"))
在更复杂的情况下,你需要确保基础目录存在,因为os.Create不会处理它。
这就是为什么os.Create("~/golang-server.log")会失败的原因,因为基础目录不存在,而os.Create不会自动为你创建它。你需要像这样自己创建:
func EnsureBaseDir(fpath string) error {
    baseDir := path.Dir(fpath)
    info, err := os.Stat(baseDir)
    if err == nil && info.IsDir() {
        return nil
    }
    return os.MkdirAll(baseDir, 0755)
}
注意:
$HOME并不总是存在,例如程序作为系统级别的systemd服务运行。因此,在生产环境中最好不要使用$HOME。os.Create的限制不仅存在于Go中,它是由操作系统内核引起的,在其他语言/标准库中也广泛存在。
英文:
You can use  os.ExpandEnv to handle the "~" which is only make sense in sh/bash context.
os.Create(os.ExpandEnv("$HOME/golang-server.log"))
In more complicated cases, you need to ensure that the base directory exists, because os.Create does not handle it.
And this is the reason why os.Create("~/golang-server.log") will fail, because the base directory doesn't exist and os.Create wouldn't automatically create it for you. You need do it by yourself like this:
func EnsureBaseDir(fpath string) error {
baseDir := path.Dir(fpath)
info, err := os.Stat(baseDir)
if err == nil && info.IsDir() {
return nil
}
return os.MkdirAll(baseDir, 0755)
}
NOTE:
$HOMEis not always exists, e.g. the program run as a system levelsystemdservice. So you better not use$HOMEin a production environment.- the limitation of 
os.Createdoesn't only exist in Go. It's caused by the OS kernel and widely exists in other languages/stdlib. 
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论