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语言实现一个简单的压测工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录整体架构通用数据处理模块Http请求响应数据处理Curl参数解析处理客户端模块Http客户端处理Grpc客户端处理Websocket客户端

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操

Go语言实现将中文转化为拼音功能

《Go语言实现将中文转化为拼音功能》这篇文章主要为大家详细介绍了Go语言中如何实现将中文转化为拼音功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 有这么一个需求:新用户入职 创建一系列账号比较麻烦,打算通过接口传入姓名进行初始化。想把姓名转化成拼音。因为有些账号即需要中文也需要英

Go语言使用Buffer实现高性能处理字节和字符

《Go语言使用Buffer实现高性能处理字节和字符》在Go中,bytes.Buffer是一个非常高效的类型,用于处理字节数据的读写操作,本文将详细介绍一下如何使用Buffer实现高性能处理字节和... 目录1. bytes.Buffer 的基本用法1.1. 创建和初始化 Buffer1.2. 使用 Writ

Go Gorm 示例详解

《GoGorm示例详解》Gorm是一款高性能的GolangORM库,便于开发人员提高效率,本文介绍了Gorm的基本概念、数据库连接、基本操作(创建表、新增记录、查询记录、修改记录、删除记录)等,本... 目录1. 概念2. 数据库连接2.1 安装依赖2.2 连接数据库3. 数据库基本操作3.1 创建表(表关

Go信号处理如何优雅地关闭你的应用

《Go信号处理如何优雅地关闭你的应用》Go中的优雅关闭机制使得在应用程序接收到终止信号时,能够进行平滑的资源清理,通过使用context来管理goroutine的生命周期,结合signal... 目录1. 什么是信号处理?2. 如何优雅地关闭 Go 应用?3. 代码实现3.1 基本的信号捕获和优雅关闭3.2

Go Playground 在线编程环境

For all examples in this and the next chapter, we will use Go Playground. Go Playground represents a web service that can run programs written in Go. It can be opened in a web browser using the follow

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

go基础知识归纳总结

无缓冲的 channel 和有缓冲的 channel 的区别? 在 Go 语言中,channel 是用来在 goroutines 之间传递数据的主要机制。它们有两种类型:无缓冲的 channel 和有缓冲的 channel。 无缓冲的 channel 行为:无缓冲的 channel 是一种同步的通信方式,发送和接收必须同时发生。如果一个 goroutine 试图通过无缓冲 channel

如何确定 Go 语言中 HTTP 连接池的最佳参数?

确定 Go 语言中 HTTP 连接池的最佳参数可以通过以下几种方式: 一、分析应用场景和需求 并发请求量: 确定应用程序在特定时间段内可能同时发起的 HTTP 请求数量。如果并发请求量很高,需要设置较大的连接池参数以满足需求。例如,对于一个高并发的 Web 服务,可能同时有数百个请求在处理,此时需要较大的连接池大小。可以通过压力测试工具模拟高并发场景,观察系统在不同并发请求下的性能表现,从而