go 切换当前运行目录_如何获取用户主目录?os/user 就可以了,为什么要 gohomedir... - Go语言中文社区

go 切换当前运行目录_如何获取用户主目录?os/user 就可以了,为什么要 gohomedir...


简介

今天我们来看一个很小,很实用的库go-homedir。顾名思义,go-homedir用来获取用户的主目录。实际上,使用标准库os/user我们也可以得到这个信息:

package main

import (
"fmt"
"log"
"os/user"
)

func main() {
u, err := user.Current()
if err != nil {
log.Fatal(err)
}

fmt.Println("Home dir:", u.HomeDir)
}

那么为什么还要go-homedir库?

在 Darwin 系统上,标准库os/user的使用需要 cgo。所以,任何使用os/user的代码都不能交叉编译。但是,大多数人使用os/user的目的仅仅只是想获取主目录。因此,go-homedir库出现了。

快速使用

go-homedir是第三方包,使用前需要先安装:

$ go get github.com/mitchellh/go-homedir

使用非常简单:

package main

import (
"fmt"
"log"

"github.com/mitchellh/go-homedir"
)

func main() {
dir, err := homedir.Dir()
if err != nil {
log.Fatal(err)
}

fmt.Println("Home dir:", dir)

dir = "~/golang/src"
expandedDir, err := homedir.Expand(dir)
if err != nil {
log.Fatal(err)
}

fmt.Printf("Expand of %s is: %sn", dir, expandedDir)
}

go-homedir有两个功能:

  • Dir:获取用户主目录;

  • Expand:将路径中的第一个~扩展成用户主目录。

高级用法

由于Dir的调用可能涉及一些系统调用和外部执行命令,多次调用费性能。所以go-homedir提供了缓存的功能。默认情况下,缓存是开启的。我们也可以将DisableCache设置为false来关闭它。

package main

import (
"fmt"
"log"

"github.com/mitchellh/go-homedir"
)

func main() {
homedir.DisableCache = false

dir, err := homedir.Dir()
if err != nil {
log.Fatal(err)
}

fmt.Println("Home dir:", dir)
}

使用缓存时,如果程序运行中修改了主目录,再次调用Dir还是返回之前的目录。如果需要获取最新的主目录,可以先调用Reset清除缓存。

实现

go-homedir源码只有一个文件homedir.go,今天我们大概看一下Dir的实现,去掉缓存相关代码:

func Dir() (string, error) {
var result string
var err error
if runtime.GOOS == "windows" {
result, err = dirWindows()
} else {
// Unix-like system, so just assume Unix
result, err = dirUnix()
}

if err != nil {
return "", err
}
return result, nil
}

判断当前的系统是windows还是类 Unix,分别调用不同的方法。先看 windows 的,比较简单:

func dirWindows() (string, error) {
// First prefer the HOME environmental variable
if home := os.Getenv("HOME"); home != "" {
return home, nil
}

// Prefer standard environment variable USERPROFILE
if home := os.Getenv("USERPROFILE"); home != "" {
return home, nil
}

drive := os.Getenv("HOMEDRIVE")
path := os.Getenv("HOMEPATH")
home := drive + path
if drive == "" || path == "" {
return "", errors.New("HOMEDRIVE, HOMEPATH, or USERPROFILE are blank")
}

return home, nil
}

流程如下:

  • 读取环境变量HOME,如果不为空,返回这个值;

  • 读取环境变量USERPROFILE,如果不为空,返回这个值;

  • 读取环境变量HOMEDRIVEHOMEPATH,如果两者都不为空,拼接这两个值返回。

类 Unix 系统的实现稍微复杂一点:

func dirUnix() (string, error) {
homeEnv := "HOME"
if runtime.GOOS == "plan9" {
// On plan9, env vars are lowercase.
homeEnv = "home"
}

// First prefer the HOME environmental variable
if home := os.Getenv(homeEnv); home != "" {
return home, nil
}

var stdout bytes.Buffer

// If that fails, try OS specific commands
if runtime.GOOS == "darwin" {
cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`)
cmd.Stdout = &stdout
if err := cmd.Run(); err == nil {
result := strings.TrimSpace(stdout.String())
if result != "" {
return result, nil
}
}
} else {
cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid()))
cmd.Stdout = &stdout
if err := cmd.Run(); err != nil {
// If the error is ErrNotFound, we ignore it. Otherwise, return it.
if err != exec.ErrNotFound {
return "", err
}
} else {
if passwd := strings.TrimSpace(stdout.String()); passwd != "" {
// username:password:uid:gid:gecos:home:shell
passwdParts := strings.SplitN(passwd, ":", 7)
if len(passwdParts) > 5 {
return passwdParts[5], nil
}
}
}
}

// If all else fails, try the shell
stdout.Reset()
cmd := exec.Command("sh", "-c", "cd && pwd")
cmd.Stdout = &stdout
if err := cmd.Run(); err != nil {
return "", err
}

result := strings.TrimSpace(stdout.String())
if result == "" {
return "", errors.New("blank output when reading home directory")
}

return result, nil
}

流程如下:

  • 先读取环境变量HOME(注意 plan9 系统上为home),如果不为空,返回这个值;

  • 使用getnet命令查看系统的数据库中的相关记录,我们知道passwd文件中存储了用户信息,包括用户的主目录。使用getent命令查看passwd中当前用户的那条记录,然后从中找到主目录部分返回;

  • 如果上一个步骤失败了,我们知道cd后不加参数是直接切换到用户主目录的,而pwd可以显示当前目录。那么就可以结合这两个命令返回主目录。

这里分析源码并不是表示使用任何库都要熟悉它的源码,毕竟使用库就是为了方便开发。 但是源码是我们学习和提高的一个非常重要的途径。我们在使用库遇到问题的时候也要有能力从文档或甚至源码中查找原因。

参考

  1. home-dir GitHub 仓库

推荐阅读


喜欢本文的朋友,欢迎关注“Go语言中文网”:

6efc7d27704a904e7122de47be5b4483.png

Go语言中文网启用微信学习交流群,欢迎加微信:274768166

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_33204522/article/details/113074799
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-06-14 10:36:47
  • 阅读 ( 1137 )
  • 分类:Go

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢