Golang: Операционная система и ввод-вывод

Функции ОС для повседневного использования

При работе над любой программой Go нам понадобятся некоторые функции ОС для работы с системной средой. В этой статье давайте рассмотрим некоторые из основных функций, предоставляемых пакетом os.

Встроенный os пакет, предоставляемый Go, дает нам возможность доступа к встроенным функциям операционной системы независимо от платформы. К ним относятся создание процессов, изменение прав пользователей, файловый ввод-вывод и многое другое.

В этом руководстве мы рассмотрим некоторые основные функции, экспортированные из этого пакета, которые помогут нам в повседневных случаях использования.

Глобальные константы и переменные

Пакет os экспортирует некоторые общие переменные и постоянные значения.

os.PathSeparator

Константа os.PathSeparator возвращает значение ASCII int разделителя пути. в Unix-подобных системах разделителем путей является /, а в случае Windows - \. Вы можете найти список разделителей путей отсюда.

Поскольку os.PathSeparator возвращает значение int, нам нужно преобразовать его в символ с помощью команды форматирования %c. Приведенная выше программа дает такой результат.

🌶 go run go-os.go
os.PathSeparator => 47
os.PathSeparator => /

os.DevNull

константа os.DevNull возвращает string, указывающее нулевое устройство операционной системы. Это загадочное виртуальное устройство, которое может потреблять анданные, но нигде не хранится, прямо как черная дыра.

В Unix-подобных системах нулевым устройством по умолчанию является /dev/null, а в случае Windows - NUL. Вы можете узнать больше о нулевом устройстве отсюда. В моем случае (macOS) приведенная выше программа дает следующий результат.

🌶 go run go-os.go
os.DevNull => /dev/null

os.Args

Переменная os.Args возвращает slice строк, представляющих аргументы, переданные команде, запустившей выполнение программы. Мы обсуждали эту переменную в нашем уроке выполнение команд оболочки.

🌶 go run go-os.go value key=value --flag
os.Args =>  [/var/folders/.../go-os value key=value --flag]

Как видно из приведенного выше результата, первый элемент os.Args всегда является местоположением двоичного исполняемого файла. Остальные аргументы - это настраиваемые значения, передаваемые команде, поэтому выражение os.Args[1:] может быть очень удобным для получения всех аргументов командной строки.

💡 При создании приложения CLI переменная os.Args может быть очень полезной для получения аргументов командной строки пользователя, как мы видели выше.

os.Stdin / os.Stdout / os.Stderr

os.Stdin, os.Stdout и os.Stderr - это открытые файлы в памяти, которые указывают на стандартные потоки ввода-вывода операционной системы. Например, os.Stdin может читать со стандартного устройства ввода, такого как клавиатура, а os.Stdout может писать на стандартное устройство вывода, такое как терминальная консоль.

Мы обсудили варианты использования этих потоков в уроке « выполнение команд оболочки ». Вы можете узнать больше о стандартных потоках в этой статье в Википедии.

Выход из процесса с помощью os.Exit

Функция os.Exit(code int) завершает текущий процесс и выходит из программы с code целочисленным кодом состояния. Код состояния0 указывает, что процесс был завершен успешно, а код состояния 1 указывает, что процесс завершился с общей ошибкой. Другие коды состояния могут иметь различное значение в зависимости от системы. Вы можете следить за этой поступью, чтобы узнать больше.

Мы можем вызывать os.Exit() где угодно. Этот вызов принудительно завершит процесс, но никакие отложенные функции не будут выполнены.

В приведенной выше программе мы откладываем выполнение функции, которая выводит «main completed!» при выходе из метода main. Затем мы задерживаем выполнение функции на 2 секунды, которая существует в процессе с кодом состояния 1. Затем основная горутина переходит в спящий режим на 3 секунды.

Поскольку вызов time.AfterFunc выполняет функцию через 2 секунды в своей собственной горутине, процесс немедленно завершается, а отложенная функция никогда не вызывается. Эта программа дает следующий результат.

🌶 go run go-os.go
main() started!
exit status 1

💡 Мы также можем использовать функцию Os.Getpid для получения идентификатора процесса (pid) текущей запущенной программы. Затем мы можем использовать os.FindProcess функцию, чтобы вернуть Process с заданными pid и Process.Kill() методами, чтобы убить процесс.

Обработка переменных среды

Переменные среды могут быть очень полезны для получения информации о среде и условного выполнения некоторых операций на основе их значений. Переменная среды - это пара ключ-значение из string значений.

Обычно ключ переменной среды (name) представляет собой строку в верхнем регистре, например GOROOT или GOPATH. Вы могли использовать такую ​​команду.

GOOS=linux GOARCH=amd64 go build <package-path>

Приведенная выше команда создает двоичный исполняемый файл, зависящий от платформы, для linux/amd64 систем. Здесь GOOS - это переменная среды со значением amd64. Однако этот способ указания переменных среды работает только в Unix-подобных системах, а в случае Windows нам нужно использовать команду SET.

Go предоставляет некоторые базовые функции для чтения и записи переменных среды, не беспокоясь о базовой реализации ОС. Давайте посмотрим на эти общие функции.

os.Environ

Функция os.Environ() возвращает slice строк, содержащих пары "ключ-значение" переменных среды текущего запущенного процесса.

В приведенной выше программе мы просто сохраняем все переменные среды в env и перебираем его с помощью цикла for-range. Поскольку переменная среды представлена ​​в форме name=value, мы можем извлечь name и value по отдельности, разделив ее символом = с помощью функции strings.Split.

🌶 go run go-os.go
[0] USER => Uday.Hiwarale
[1] PATH => /Users/Uday.Hiwarale/....
[2] LOGNAME => Uday.Hiwarale
[4] HOME => /Users/Uday.Hiwarale
[5] GOPATH => /Users/Uday.Hiwarale/....
...

os.Getenv и os.LookupEnv

Если вы хотите прочитать переменную среды, вы можете использовать функцию os.Getenv(), которая возвращает значение переменной среды. Если переменная пуста или не существует, она вернет пустой string.

func Getenv(key string) string

С другой стороны, функция os.LookupEnv() возвращает два значения. Первое значение - это значение переменной окружения, как и функция Getenv. Второе значение - это boolean значение, которое будет false, если переменная среды не существует.

func LookupEnv(key string) (string, bool)

Мы собираемся запустить эту программу с некоторыми переменными окружения. Поскольку функция os.LookupEnv() дает нам возможность проверить, существует ли переменная среды или нет, мы можем выполнять некоторые условные операции, которые невозможны в случае функции os.Getenv().

🌶 FRUIT=mango CAR= go run go-os.go
os.Getenv("FRUIT") => mango
os.Getenv("CAR") => 
os.Getenv("COUNTRY") => 
os.LookupEnv("FRUIT") => mango (exists: true)
os.LookupEnv("CAR") =>  (exists: true)
os.LookupEnv("COUNTRY") =>  (exists: false)

os.Setenv и os.Unsetenv

Функция os.Setenv устанавливает переменную среды для текущего процесса. Если процесс запускает другой подпроцесс, нам необходимо передать эти переменные среды этому процессу вручную.

func Setenv(key, value string) error

Если при установке переменной среды возникнет ошибка, она вернет error. Если переменная среды уже существует, ее значение будет переопределено.

Точно так же мы можем удалить переменную среды из текущего процесса, используя функцию Unsetenv, которая может возвращать ошибку. Если переменная среды, которую мы пытаемся сбросить, не существует, ошибка не возвращается.

func Unsetenv(key string) error

🌶 FRUIT=mango go run go-os.go
Setenv(FRUIT) => error(<nil>)
Setenv(CAR) => error(<nil>)
Env: FRUIT/CAR => banana/audi
Unsetenv(FRUIT) => error(<nil>)
Unsetenv(COUNTRY) => error(<nil>)
Env: FRUIT/CAR => /audi

os.Expand и os.ExpandEnv

Функция os.Expand заменяет заполнители в строке, указанной выражением $var или ${var}, с помощью функции сопоставления. Функция сопоставления считывает заполнитель и возвращает для него значение замены.

func Expand(s string, mapping func(string) string) string

В приведенной выше программе у нас есть строка raw с некоторыми заполнителями. Мы создали функцию сопоставления mapper, которая возвращает замещающие значения для этих заполнителей. Теперь мы можем использовать функцию os.Expand для преобразования этих заполнителей в реальные значения.

🌶 go run go-os.go 
I am eating mango and driving audi.

Функция os.Expand может быть очень полезна для обычного текстового редактора, однако функция os.ExpandEnv очень полезна для обработки необработанной строки с именами переменных среды в качестве заполнителей.

func ExpandEnv(s string) string

Как мы видим, функции ExpandEnv не требуется функция сопоставления. Эта функция внутренне вызывает функцию os.Expand с os.Getenv в качестве функции сопоставления, поэтому значение-заполнитель на самом деле является значением переменной среды в текущем процессе.

🌶 FRUIT=mango CAR=audi go run go-os.go
I am eating mango and driving audi in italy.

Получите информацию о системе и пользователях

Иногда нам нужна основная информация о пользователе, который запускает программу, например, имя пользователя и его / ее домашний каталог для доступа или записи некоторых файлов.

os.Hostname

Эта os.Hostname функция возвращает имя хоста (bash hostname команда) системы, в которой запущена программа. Эта функция может возвращать error в случае, если системное имя хоста недоступно из ядра.

func Hostname() (name string, err error)

🌶 go run go-os.go
Udays-MBP.local <nil>

os.UserHomeDir

Функция os.UserHomeDir возвращает домашний каталог текущего пользователя. Домашний каталог - это место, где текущий пользователь, вошедший в систему, может делать абсолютно все. Это похоже на использование переменной среды $HOME в Unix-подобных системах.

func UserHomeDir() (string, error)

🌶 go run go-os.go
/Users/Uday.Hiwarale <nil>

ОС / пользовательский пакет

Чтобы получить дополнительную информацию о текущем вошедшем в систему пользователе, системных пользователях и группах пользователей, нам нужно импортировать os/user package.

Мы можем использовать функцию user.Current(), чтобы получить информацию о текущем вошедшем в систему пользователе. Вы также можете использовать user.Lookup(username) метод, чтобы найти пользователя с определенным именем пользователя. Оба эти метода возвращают структуру User.

type User struct {
  Uid string // the user ID
  Gid string // the primary group ID
  Username string // the login name
  Name string // user's real or display name
  HomeDir string // user's home directory
}

🌶 go run go-os.go
&user.User{Uid:"502", Gid:"20", Username:"Uday.Hiwarale", Name:"Uday Hiwarale", HomeDir:"/Users/Uday.Hiwarale"}
err: <nil>

Рабочие каталоги

В типичной программе, которой требуется доступ к собственной файловой системе, мы работаем либо в текущем рабочем каталоге, где терминал (консоль) запустил программу Go, либо в каталоге, в котором двоичный файл программы находится.

os.Getwd и os.Chdir

В bash мы можем использовать команду pwd для получения текущего рабочего каталога. Мы также можем использовать команду cd для изменения рабочего каталога.

В Go пакет os предоставляет функцию Getwd для получения текущего рабочего каталога и функцию Chdir для изменения рабочего каталога.

func Getwd() (dir string, err error)
func Chdir(dir string) error

🌶 go run go-os.go
[before] cwd: /Users/Uday.Hiwarale/uday-gh/go-os
[after] cwd: /Users/Uday.Hiwarale/uday-gh
[after] cwd: /Users/Uday.Hiwarale

Как видно из приведенного выше результата, путь к функции Chdir может быть относительным или абсолютным. Если путь, переданный функции os.Chdir, является относительным путем, он будет считаться относительно текущего рабочего каталога. Если текущий каталог не может быть установлен на указанный путь, функция Chdir может вернуть error.

os. исполняемый

Go - это скомпилированный язык, что означает, что программу можно запустить, только сначала скомпилировав исходный код в двоичный исполняемый файл, а затем запустив этот исполняемый файл. Кроме того, когда мы отправляем программное обеспечение, написанное на Go, мы отправляем двоичные исполняемые файлы, скомпилированные для определенных системных архитектур.

При работе с исходными файлами программы у нас есть привычка использовать относительные пути к файлам в системе. Относительный путь в Go (как ../../file.txt) разрешен по отношению к текущему рабочему каталогу.

В некоторых случаях нам нужно найти файл относительно самого исполняемого файла Go. Например, если вы передаете клиенту исполняемый файл вместе с файлом file.txt, и ваша программа зависит от этого файла, то они, вероятно, находятся в одном каталоге.

Поскольку исполняемый файл может быть запущен из любого места в системе, рабочий каталог всегда меняется, и это не может помочь нам найти file.txt файл. Однако расположение исполняемых файлов всегда постоянно.

Нам нужен путь к исполняемому файлу в самой запущенной программе, чтобы найти file.txt. Функция os.Executable возвращает абсолютный путь к запущенному исполняемому файлу в системе.

func Executable() (string, error)

Мы можем использовать пакет path или path/filepath для присоединения и разрешения файла в системе относительно текущего исполняемого файла.

Когда указанная выше программа запускается с помощью команды go run, Go сначала скомпилирует исходный код и создаст двоичный исполняемый файл во временном расположении. Затем он запустит этот исполняемый файл, чтобы получить следующий результат.

🌶 go run go-os.go
exeDir /var/folders/jd/mvgp/T/go-build27/b001/exe/go-os

Однако, если мы сами скомпилируем программу в текущем каталоге, мы получим другой результат. На этот раз результат предсказуем.

🌶 go build go-os.go
🌶 ./go-os 
exeDir /Users/Uday.Hiwarale/uday-gh/go-os/go-os

os.TempDir

Когда программе необходимо подготовить некоторые файлы для предварительной обработки перед их сбросом в конечный каталог, нам необходимо сохранить их в системном каталоге, где пользователь не может их видеть. и система может безопасно собирать их в будущем.

func TempDir() string

Функция os.TempDir возвращает временный каталог системы. Однако не гарантируется, что в системе всегда есть временный каталог или программа Go будет иметь достаточные разрешения для его использования.

🌶 go run go-os.go
tempDir /var/folders/jd/mvgp/T/

💡 Если вы ищете способ создать временный каталог или временный файл, вы можете использовать функции ioutil.TempDir() и ioutil.TempFile().