Go 每日一库之 go-homedir

2023-12-25 17:32
文章标签 go 每日 一库 homedir

本文主要是介绍Go 每日一库之 go-homedir,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

简介

米妮 m.amini.net

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

package mainimport ("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 mainimport ("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: %s\n", dir, expandedDir)
}

go-homedir有两个功能:

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

高级用法

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

package mainimport ("fmt""log""github.com/mitchellh/go-homedir"
)func main() {homedir.DisableCache = falsedir, 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 stringvar err errorif runtime.GOOS == "windows" {result, err = dirWindows()} else {// Unix-like system, so just assume Unixresult, err = dirUnix()}if err != nil {return "", err}return result, nil
}

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

func dirWindows() (string, error) {// First prefer the HOME environmental variableif home := os.Getenv("HOME"); home != "" {return home, nil}// Prefer standard environment variable USERPROFILEif home := os.Getenv("USERPROFILE"); home != "" {return home, nil}drive := os.Getenv("HOMEDRIVE")path := os.Getenv("HOMEPATH")home := drive + pathif 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 variableif home := os.Getenv(homeEnv); home != "" {return home, nil}var stdout bytes.Buffer// If that fails, try OS specific commandsif runtime.GOOS == "darwin" {cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`)cmd.Stdout = &stdoutif 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 = &stdoutif 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:shellpasswdParts := strings.SplitN(passwd, ":", 7)if len(passwdParts) > 5 {return passwdParts[5], nil}}}}// If all else fails, try the shellstdout.Reset()cmd := exec.Command("sh", "-c", "cd && pwd")cmd.Stdout = &stdoutif 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 仓库

我的博客

欢迎关注我的微信公众号【GoUpUp】,共同学习,一起进步~

1919725-20200115064328344-1414002693.jpg

本文由博客一文多发平台 OpenWrite 发布!

这篇关于Go 每日一库之 go-homedir的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/536332

相关文章

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

Go语言并发之通知退出机制的实现

《Go语言并发之通知退出机制的实现》本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、通知退出机制1.1 进程/main函数退出1.2 通过channel退出1.3 通过cont

Go语言编译环境设置教程

《Go语言编译环境设置教程》Go语言支持高并发(goroutine)、自动垃圾回收,编译为跨平台二进制文件,云原生兼容且社区活跃,开发便捷,内置测试与vet工具辅助检测错误,依赖模块化管理,提升开发效... 目录Go语言优势下载 Go  配置编译环境配置 GOPROXYIDE 设置(VS Code)一些基本

使用Go实现文件复制的完整流程

《使用Go实现文件复制的完整流程》本案例将实现一个实用的文件操作工具:将一个文件的内容完整复制到另一个文件中,这是文件处理中的常见任务,比如配置文件备份、日志迁移、用户上传文件转存等,文中通过代码示例... 目录案例说明涉及China编程知识点示例代码代码解析示例运行练习扩展小结案例说明我们将通过标准库 os

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

go中的时间处理过程

《go中的时间处理过程》:本文主要介绍go中的时间处理过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 获取当前时间2 获取当前时间戳3 获取当前时间的字符串格式4 相互转化4.1 时间戳转时间字符串 (int64 > string)4.2 时间字符串转时间

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

Go语言中nil判断的注意事项(最新推荐)

《Go语言中nil判断的注意事项(最新推荐)》本文给大家介绍Go语言中nil判断的注意事项,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.接口变量的特殊行为2.nil的合法类型3.nil值的实用行为4.自定义类型与nil5.反射判断nil6.函数返回的

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

Go语言代码格式化的技巧分享

《Go语言代码格式化的技巧分享》在Go语言的开发过程中,代码格式化是一个看似细微却至关重要的环节,良好的代码格式化不仅能提升代码的可读性,还能促进团队协作,减少因代码风格差异引发的问题,Go在代码格式... 目录一、Go 语言代码格式化的重要性二、Go 语言代码格式化工具:gofmt 与 go fmt(一)