go-homedir из ежедневной библиотеки Go

Go

Введение

Сегодня мы рассмотрим небольшую, очень полезную библиотеку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-подобный вызов различных методов соответственно. Сначала взгляните на окна, это относительно просто:

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, если не пусто, вернуть это значение;
  • читать переменные окруженияHOMEDRIVEа такжеHOMEPATH, если ни одно из них не пусто, объедините два значения и вернитесь.

Реализация на 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

я

мой блог

Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись WeChat [GoUpUp], учитесь вместе и добивайтесь прогресса вместе ~

Эта статья опубликована в блогеOpenWriteвыпускать!