Go 每日一库之 go-homedir

栏目: IT技术 · 发布时间: 4年前

内容简介:今天我们来看一个很小,很实用的库实际上,使用标准库那么为什么还要

简介

今天我们来看一个很小,很实用的库 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: %s\n", 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 仓库

我的博客

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

Go 每日一库之 go-homedir

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


以上所述就是小编给大家介绍的《Go 每日一库之 go-homedir》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Think Python

Think Python

Allen B. Downey / O'Reilly Media / 2012-8-23 / GBP 29.99

Think Python is an introduction to Python programming for students with no programming experience. It starts with the most basic concepts of programming, and is carefully designed to define all terms ......一起来看看 《Think Python》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具