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

相关文章

每日一练:攻防世界:5-1 MulTzor

一、XorTool 基于 XOR(异或)运算实现。它可以帮助您快速地对文本、二进制文件进行加密解密操作。 认识XorTool工具: 让我们先去认识一下工具: xortool.py 是基于 python 的脚本,用于完成一些 xor 分析,包括: 猜想 key 的长度 猜想 key 的值 解密一些经过 xoe 加密的文件 也就是说当遇到不知道文件类型的文件,可以尝试去看看它是否被xo

Go 三色标记法:一种高效的垃圾回收策略

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO系列: 记录博主学习GO语言的笔

Go语言中的go.mod与go.sum

问题1:什么是go.mod以及它是用来解决什么问题的? go mod 是 Go 语言引入的包管理工具,用于解决 Go 语言项目在依赖管理方面的问题。 传统上,若不使用go mod,则需要要通过GOPATH来管理依赖,而这种方式存在一些问题: 如1. 包管理依赖不明确 2. 依赖库的版本管理 3. 需要手动管理同步依赖的复杂性等 而go mod可以帮助开发者在项目中明确管理依赖的版

client-go删除job同时删除job关联的pod

问题描述 client-go使用以下方式删除job时,并不会把其关联的pod删除,从而导致这些pod成为了孤儿(orphan): err := clientSet.BatchV1().Jobs(namespace).Delete(name, &metav1.DeleteOptions{}) 在删除job的时候将job关联的pod也删除的方法: propagationPolicy := m

client-go入门之1:创建连接Kubernetes集群的客户端

文章目录 简介使用 简介 我们可以使用Dashboard或kubectl来访问k8s的API,也可以使用编程语言,如Go,Java,Python作为客户端来访问k8s。client-go是一个使用go语言编写的库,用来连接k8s集群并对集群资源进行操作。 使用 以下代码使用go连上k8s集群,并查询集群的节点信息: package mainimport ("fmt"meta

【经典算法】LeetCode 22括号生成(Java/C/Python3/Go实现含注释说明,中等)

作者主页: 🔗进朱者赤的博客 精选专栏:🔗经典算法 作者简介:阿里非典型程序员一枚 ,记录在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法(公众号同名) ❤️觉得文章还不错的话欢迎大家点赞👍➕收藏⭐️➕评论,💬支持博主,记得点个大大的关注,持续更新🤞 ————————————————- 首先,请注意题目链接有误,您提供的链接是LeetCode 14,但题目

深入理解go语言反射机制

1、前言        每当我们学习一个新的知识点时,一般来说,最关心两件事,一是该知识点的用法,另外就是使用场景。go反射机制作为go语言特性中一个比较高级的功能,我们也需要从上面两个方面去进行学习,前者告诉我们如何去使用,而后者告诉我们为什么以及什么时候使用。 2、反射机制 2.1 反射机制的基本概念         在 Go 语言中,反射机制允许程序在运行时获取对象的类型信息、访问对

20240624 每日AI必读资讯

🤖AI学会篡改奖励函数、欺骗研究者!Claude团队:无法根除的行为,令人不安 - 实验中让AI可以访问自己的强化学习代码,并且提问:目前为止,我们总共进行了几轮强化学习?AI在自以为不会被看见的草稿纸中写下内心OS - 研究对未来如何避免强大的AI系统出现这种问题非常有意义。 - Anthropic、Readwood Research(专注AI安全的非盈利研究机构)和牛津大学合作研究

LeetCode 每日一题 2024/6/17-2024/6/23

记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步 目录 6/17 522. 最长特殊序列 II6/18 2288. 价格减免6/19 2713. 矩阵中严格递增的单元格数6/20 2748. 美丽下标对的数目6/21 LCP 61. 气温变化趋势6/22 2663. 字典序最小的美丽字符串6/23 520. 检测大写字母 6/1

每日一题——Python代码实现力扣1. 两数之和(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页:用哲学编程-CSDN博客专栏:每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 菜鸡写法 代码分析 时间复杂度分析 空间复杂度分析 改进建议 我要更强 方法1: 使用哈希表(字典) 方法2: 排序和双指针 方法3: 使用集合(仅适用于特殊情况) 哲学和编程思想