Как создать go файл

Обновлено: 06.07.2024

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

В Go процесс перевода исходного кода в двоичный исполняемый файл называется сборкой. В готовый исполняемый файл входит не только ваше приложение, но и весь вспомогательный код, необходимый для выполнения двоичного файла на целевой платформе. То есть для запуска в новой системе двоичный файл Go не требует системных зависимостей, в отличие от других языков, таких как Ruby, Python или Node.js. Помещая такие файлы в исполняемый путь системы, вы можете запустить программу из любой точки вашей системы. Это называется установкой программы.

В этом мануале вы научитесь использовать набор инструментов Go для запуска, сборки и установки простой программы «Hello, World!». Этот простой пример поможет вам эффективно использовать, распространять и развертывать ваши будущие приложения.

Требования

Чтобы выполнить этот мануал, вам понадобится среда разработки. Создать такую среду вам помогут наши мануалы:

Создание и запуск тестовой программы Go

Сначала создайте тестовое приложение, на котором вы сможете потренироваться работать с набором инструментов Go. Для этого мы создадим простую классическую программу «Hello, World!».

Создайте каталог greeter в каталоге src:

Затем перейдите в новый каталог и создайте файл main.go в любом текстовом редакторе:

cd greeter
nano main.go

Вставьте в файл следующие строки:

package main
import "fmt"
func main() fmt.Println("Hello, World!")
>

При запуске эта программа выведет фразу Hello, World! на консоль и затем успешно завершит работу.

Сохраните файл и выйдите из него.

Чтобы протестировать программу, используйте команду go run:

Вы получите следующий вывод:

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

Сборка бинарных файлов Go с помощью go build

Используя команду go build, вы можете сгенерировать исполняемый двоичный файл нашего тестового приложения Go, что позволит вам распространять и развертывать программу.

Попробуйте собрать main.go. В своем каталоге greeter выполните следующую команду:

Если вы не предоставите этой команде аргумент, go build автоматически скомпилирует программу main.go в текущем каталоге. Команда включит все файлы *.go в каталоге. Она также создаст весь вспомогательный код, необходимый для выполнения двоичного файла на любом компьютере с такой же архитектурой системы (независимо от того, имеет ли эта система исходные файлы .go и установлен ли в ней Go).

В этом случае вы собрали приложение greeter в исполняемый файл, который был добавлен в текущий каталог. Чтобы проверить это, можно выполнить команду ls:

Если вы работаете в macOS или Linux, вы найдете новый исполняемый файл, имя которого будет совпадать с именем каталога, в котором вы собрали свою программу:

Примечание: В Windows файл будет называться greeter.exe.

По умолчанию go build генерирует исполняемый файл для текущей платформы и архитектуры. Например, если исполняемый файл собирается в системе linux/386, он будет совместим с любой другой системой linux/386, даже если в ней не установлен Go. Go поддерживает сборку для других платформ и архитектур.

Теперь, когда вы собрали свой исполняемый файл, запустите его, чтобы убедиться, что он собран правильно. В macOS или Linux запустите следующую команду:

В Windows запустите:

Вывод двоичного файла совпадает с выводом, который вы получили при запуске программы с помощью go run:

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

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

Переименование бинарного файла

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

Когда вы запускаете go build, по умолчанию Go автоматически выбирает имя сгенерированного исполняемого файла. Это делается одним из двух способов: если вы используете модули Go, то Go будет использовать последнюю часть имени вашего модуля; в противном случае Go будет использовать имя текущего каталога. Последний метод использовался в предыдущем разделе, когда мы создали каталог greeter, перешли в него, а затем запустили go build.

Давайте подробнее рассмотрим первый метод – работу с модулями. Если бы в вашем проекте был файл go.mod с объявлением модуля, например:

то по умолчанию вашему файлу было бы присвоено имя blog.

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

Давайте посмотрим, как это работает. Измените имя исполняемого файла, созданного вами в предыдущем разделе, на hello и поместите его в подкаталог bin. Вам не нужно создавать его, Go сделает это самостоятельно в процессе сборки.

Запустите следующую команду go build с флагом -o:

go build -o bin/hello

Благодаря флагу –о вы получите новый исполняемый файл по имени hello в подпапке bin.

Чтобы проверить новый исполняемый файл, перейдите в новый каталог и запустите файл:

Вы получите такой результат:

Теперь вы можете настроить имя вашего исполняемого файла в соответствии с потребностями вашего проекта,

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

Установка программы Go с помощью go install

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

Попробуйте использовать команду go install для установки нашего тестового приложения.

Команда go install ведет себя почти так же, как go build, но она не оставляет исполняемый файл в текущем каталоге или в каталоге, указанном флагом -o, она помещает исполняемый файл в каталог $GOPATH/bin.

Чтобы узнать, где находится каталог $GOPATH, выполните следующую команду:

Ваш вывод может отличаться, но по умолчанию это каталог go в вашем каталоге $HOME:

Поскольку go install поместит сгенерированные исполняемые файлы в подкаталог $GOPATH по имени bin, этот каталог необходимо добавить в переменную среды $PATH. Сделать это вам помогут мануалы, перечисленные в разделе «Требования». Установив каталог $GOPATH/bin, вернитесь в каталог greeter:

Теперь запустите команду установки:

Она соберет двоичный файл и поместит его в $GOPATH/bin. Чтобы убедиться, что все прошло успешно, запустите:

Эта команда перечислит содержимое $GOPATH/bin:

Примечание: Команда go install не поддерживает флаг -o, поэтому она будет использовать для исполняемого файла одно из имен по умолчанию.

Установив бинарный файл, проверьте, работает ли программа вне ее исходного каталога. Вернитесь в свой домашний каталог:

Используйте следующую команду, чтобы запустить программу:

Вы получите такой вывод:

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

Заключение

Теперь вы знаете, что набор инструментов Go позволяет легко создавать исполняемые файлы из исходного кода. Эти файлы можно распространять для запуска в других системах, даже в тех, которые не имеют инструментов и сред Go. Вы также научились использовать команду go install для автоматической сборки и установки программ в виде исполняемых файлов в $PATH. Команды go build и go install позволяют вам делиться своими файлами и использовать свое приложение по своему желанию.


Чтение с диска и запись на диск, а также перемещение по файловой системе — это основной элемент в любом языке. Узнаем, как все это делать в Go с помощью пакета os, который позволяет взаимодействовать с функциональностью операционной системы.

Создание и открытие файлов

Создание файлов происходит с помощью os.Create , а открытие — с помощью os.Open . И там и там принимается путь к файлу и возвращается структура File , а в случае неуспеха — ошибка с nil .

Когда os.Create вызывается в существующем файле, он этот файл обрезает: данные файла стираются. В то же время вызов os.Open в несуществующем файле приводит к ошибке.

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

После взаимодействия с возвращенным файлом закрываем его с помощью File.Close .

Чтение файлов

Один из способов обработки файла — прочитать сразу все содержащиеся в нем данные. Делается это с использованием os.ReadFile . Вводимые данные — это путь к файлу, а выходные данные — это байтовый массив данных файла и ошибка в случае неуспеха.

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

Имейте в виду, что os.ReadFile прочитает весь файл и загрузит его данные в память. И чем больше файл, тем больший объем памяти будет потребляться при использовании os.ReadFile .

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

После открытия файла происходит многократный вызов File.Read до EOF (конца файла).

File.Read принимает байтовый массив b и загружает до len(b) байтов из файла в b . А затем возвращает количество прочитанных байтов bytesRead и ошибку, если что-то пойдет не так. При bytesRead равным 0 нажимаем EOF и заканчиваем обработку файла.

В приведенном выше коде из файла загружается максимум 10 байтов. Они обрабатываются, и этот процесс повторяется до EOF (конца файла).

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

Запись и добавление в файлы

Для записи байтов в файл существует аналогичная os.ReadFile функция os.WriteFile .

Что следует учесть при использовании os.WriteFile :

  • Обязательно преобразуйте данные для записи в []byte , прежде чем передавать их в os.WriteFile .
  • Биты полномочий необходимы для создания файла, если он еще не существует. Но на них заострять внимание не стоит.
  • Если путь к файлу уже существует, os.WriteFile переопределит исходные данные в файле с помощью новых записываемых данных.

os.WriteFile хорош для создания нового файла или его переопределения. Но он не работает, когда нужно сделать добавление к имеющемуся содержимому файла. Для добавления в файл нужно задействовать os.OpenFile .

Согласно документации, os.OpenFile — это более обобщенная версия os.Open и os.Create . И os.Create , и os.Open внутренне вызывают его.

Кроме пути к файлу, os.OpenFile принимает флаги int и perm (биты полномочий) и возвращает структуру File . Для выполнения таких операций, как чтение и запись, в os.OpenFile должна быть указана правильная комбинация флагов .

O_APPEND и O_WRONLY объединяют с побитовым ИЛИ и передают в os.OpenFile для получения структуры File . После этого при вызове File.Write с любыми передаваемыми данными эти данные будут добавлены в конец файла.

Удаление файлов

os.Remove принимает путь к файлу или пустому каталогу и удаляет этот файл/каталог. Если файл не существует, будет возвращена ошибка с nil .

Освоив основы работы с файлами, перейдем теперь к каталогам.

Создание каталогов

Для создания нового каталога используется os.Mkdir . Эта функция принимает имя каталога и биты полномочий, и так создается новый каталог. Если os.Mkdir не создаст каталог, будет возвращена ошибка с nil .

В некоторых ситуациях бывают нужны временные каталоги, которые существуют только во время выполнения программы. Для создания таких каталогов используется os.MkdirTemp .

os.MkdirTemp снабжает создаваемые временные каталоги уникальными именами, даже когда происходят вызовы от нескольких горутин или программ (источник). Закончив работу с временным каталогом, обязательно удалите вместе с его содержимым с помощью os.RemoveAll .

Чтение каталогов и перемещение по ним

Сначала с помощью os.Getwd получим текущий рабочий каталог:

В добавок к изменению рабочего каталога у нас есть возможность получить дочерний каталог. Делается это с помощью os.ReadDir . Эта функция принимает путь к каталогу и возвращает массив структур DirEntry и ошибку c nil в случае неуспеха.

Вот пример использования:

Пройдемся по каталогу

filepath.WalkDir принимает каталог root , из которого мы стартуем, и функцию обратного вызова fn следующего типа:

fn будет вызываться в каждом файле и подкаталоге каталога root . Вот пример подсчета всех файлов в корневом каталоге:

Вместо того, чтобы каждый раз писать всё с нуля, реальный мир программирования требует от нас умения взаимодействовать с уже существующими библиотеками. В этой главе мы рассмотрим самые часто используемые пакеты, включенные в Go.

Предупреждаю: некоторые библиотеки достаточно очевидны (или были объяснены в предыдущих главах), многие из библиотек, включённых в Go требуют специальных знаний (например: криптография). Объяснение этих технологий выходит за рамки этой книги.

Строки

Go содержит большое количество функций для работы со строками в пакете strings :

Иногда нам понадобится работать с бинарными данными. Чтобы преобразовать строку в набор байт (и наоборот), выполните следующие действия:

Ввод / Вывод

Прежде чем мы перейдем к работе с файлами, нужно узнать про пакет io . Пакет io состоит из нескольких функций, но в основном, это интерфейсы, используемые в других пакетах. Два основных интерфейса — это Reader и Writer . Reader занимается чтением с помощью метода Read . Writer занимается записью с помощью метода Write . Многие функции принимают в качестве аргумента Reader или Writer . Например, пакет io содержит функцию Copy , которая копирует данные из Reader во Writer :

Чтобы прочитать или записать []byte или string , можно использовать структуру Buffer из пакета bytes :

Buffer не требует инициализации и поддерживает интерфейсы Reader и Writer . Вы можете конвертировать его в []byte вызвав buf.Bytes() . Если нужно только читать строки, можно так же использовать функцию strings.NewReader() , которая более эффективна, чем чтение в буфер.

Файлы и папки

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

Мы используем defer file.Close() сразу после открытия файла, чтобы быть уверенным, что файл будет закрыт после выполнения функции. Чтение файлов является частым действием, так что вот самый короткий способ сделать это:

А вот так мы можем создать файл:

Чтобы получить содержимое каталога, мы используем тот же os.Open() , но передаём ему путь к каталогу вместо имени файла. Затем вызывается функция Readdir :

Иногда мы хотим рекурсивно обойти каталоги (прочитать содержимое текущего и всех вложенных каталогов). Это делается просто с помощью функции Walk , предоставляемой пакетом path/filepath :

Функция, передаваемая вторым аргументом, вызывается для каждого файла и каталога в корневом каталоге (в данном случае).

Ошибки

Контейнеры и сортировки

В дополнение к спискам и картам, Go предоставляет еще несколько видов коллекций, доступных в пакете container . В качестве примера рассмотрим container/list .

Список

Пакет container/list реализует двусвязный список. Структура типа данных связного списка выглядит следующим образом:


Каждый узел списка содержит значение (в нашем случае: 1, 2 или 3) и указатель на следующий узел. Но так как это двусвязный список, узел так же содержит указатель на предыдущий. Такой список может быть создан с помощью следующей программы:

Пустым значением List (вероятно, опечатка и имелось ввиду x — прим. пер.) является пустой список ( *List создаётся при вызове list.New ). Значения добавляются в список при помощи PushBack . Далее, мы перебираем каждый элемент в списке, получая ссылку на следующий, пока не достигнем nil .

Сортировка

Пакет sort содержит функции для сортировки произвольных данных. Есть несколько предопределённых функций (для срезов, целочисленных значений и чисел с плавающей точкой). Вот пример, как отсортировать ваши данные:

Хэши и криптография

Функция хэширования принимает набор данных и уменьшает его до фиксированного размера. Хэши используются в программировании повсеместно, начиная от поиска данных, заканчивая быстрым детектированием изменений. Хэш-функции в Go подразделяются на две категории: криптографические и некриптографические.

Некриптографические функции можно найти в пакете hash , который включает такие алгоритмы как adler32 , crc32 , crc64 и fnv . Вот пример использования crc32 :

Объект crc32 реализует интерфейс Writer , так что мы можем просто записать в него набор байт, как и в любой другой Writer . После записи мы вызываем Sum32() , который вернёт uint32 . Обычным применением crc32 является сравнение двух файлов. Если значение Sum32() для обоих файлов одинаковы, то, весьма вероятно (не со стопроцентной гарантией), содержимое этих файлов идентично. Если же значения отличаются, значит файлы, безусловно, разные:

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

Одним из криптографических хэш-алгоритмов является SHA-1. Вот как можно его использовать:

Этот пример очень похож на пример использования crc32 , потому что оба они реализуют интерфейс hash.Hash . Основное отличие в том, что в то время как crc32 вычисляет 32-битный хэш, sha1 вычисляет 160-битный хэш. В Go нет встроенного типа для хранения 160-битного числа, поэтому мы используем вместо него срез размером 20 байт.

Серверы

На Go очень просто создавать сетевые серверы. Сначала давайте взглянем, как создать TCP сервер:

HandleFunc обрабатывает URL-маршрут ( /hello ) с помощью указанной функции. Мы так же можем обрабатывать статические файлы при помощи FileServer :

Пакеты net/rpc (remote procedure call — удаленный вызов процедур) и net/rpc/jsonrpc обеспечивают простоту вызова методов по сети (а не только из программы, в которой они используются).

run и myfile.go являются аргументами. Мы так же можем передать команде флаги:

Пакет flag позволяет анализировать аргументы и флаги, переданные нашей программе. Вот пример программы, которая генерирует число от 0 до 6. Но мы можем изменить максимальное значение, передав программе флаг -max=100 .

Любые дополнительные не-флаговые аргументы могут быть получены с помощью flag.Args() , которая вернет []string .

Синхронизация примитивов

Мьютексы

Мьютекс (или взаимная блокировка) единовременно блокирует часть кода в одном потоке, а так же используется для защиты общих ресурсов из не-атомарных операций. Вот пример использования мьютекса:

Когда мьютекс ( m ) заблокирован из одного процесса, любые попытки повторно блокировать его из других процессов приведут к блокировке самих процессов до тех пор, пока мьютекс не будет разблокирован. Следует проявлять большую осторожность при использовании мьютексов или примитивов синхронизации из пакета sync/atomic .

Традиционное многопоточное программирование является достаточно сложным: сделать ошибку просто, а обнаружить её трудно, поскольку она может зависеть от специфичных и редких обстоятельств. Одна из сильных сторон Go в том, что он предоставляет намного более простой и безопасный способ распараллеливания задач, чем потоки и блокировки.

Язык Go имеет в своем распоряжении полный набор утилит, выполняемых из командной строки. Вы можете исполнить в терминале команду go , чтобы увидеть их:


Рисунок 1.3 Команда Go показывает подробную информацию

Все эти утилиты могут быть нам полезными. Давайте посмотрим, как пользоваться некоторыми из них.

Эта утилита компилирует тесты. По необходимости она также скомпилирует зависимости.

  • Если имя пакета не main , а, например, mymath из раздела 1.2, то после того, как Вы выполните go build , ничего не скомпилируется. Если Вы хотите скомпилировать файл .a в $GOPATH/pkg , Вам вместо этого нужно выполнить go install .
  • Если имя пакета - main , то в каталоге, откуда Вы запустили утилиту, будет создан исполняемый файл. Если Вы хотите, чтобы файл был создан в $GOPATH/bin , используйте go install или go build -o $/a.exe.
  • Если в каталоге много файлов, но Вы хотите скомпилировать только один, Вам нужно добавить имя файла после go build . Например, go build a.go . Просто go build скомпилирует все файлы в каталоге.
  • Вы также можете указать имя исполняемого файла, который будет создан. Например, в проекте mathapp (раздел 1.2), команда go build -o astaxie.exe создаст astaxie.exe вместо mathapp.exe . Именем по умолчанию для исполняемого файла является имя каталога (если пакет не main) или название первого файла-исходника (если пакет - main).

(Согласно Спецификации Языка Программирования Go имя пакета должно следовать после слова package в первой строке исходного файла. Оно не обязательно должно быть таким же, как имя каталога, и имя исполняемого файла по умолчанию будет таким же, как и имя каталога.)

go build не работает с файлами, имя которых начинается на _ или . .

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

array_linux.go | array_darwin.go | array_windows.go | array_freebsd.go

go build выберет тот, который соответствует Вашей ОС. Например, в Linux-системах он скомпилирует только array_linux.go, а все остальные файлы, начинающиеся с array_, проигнорирует.

Эта команда удаляет все файлы, созданные компилятором, включая следующие:

Я обычно пользуюсь этой командой, чтобы почистить свой проект от файлов перед тем, как загрузить его на Github. Эти файлы полезны на этапе тестов, но не нужны для контроля версий.

Те из вас, которые работают с C/C++, должны знать, что люди часто дискутируют, какой стиль написания кода лучше: K&R-стиль или ANSI-стиль. Однако же в Go есть только один стиль написания кода. Например, левые скобки могут быть только в конце строк, а не на своих отдельных строках, иначе Вы получите ошибку компиляции! К счастью, Вам не нужно запоминать все эти правила. go fmt сделает всю работу за Вас. Просто выполните команду go fmt <Имя файла>.go в терминале. Я не пользуюсь этой командой часто, поскольку среды разработки делают то же самое автоматически при сохранении проекта. О средах разработки я расскажу подробнее в следующем разделе.

go fmt - это алиас(сокращение) для gofmt -l -w для пакетов, указанных в пути команды import.

Мы обычно используем gofmt -w вместо go fmt . Последний вариант не переписывает исходники после форматирования кода. gofmt -w src форматирует весь проект.

Эта команда служит для установки удаленных пакетов. На данный момент она поддерживает BitBucket, GitHub, Google Code и Launchpad. При запуске этой команды происходят следующие вещи: первая - Go скачивает исходники пакетов, вторая - исполняется go install . Перед использованием этой команды убедитесь, что у Вас установлены соответствующие инструменты:

Для того, чтобы использовать эту команду, Вы должны корректно установить эти инструменты. Не забудьте установить переменную $PATH . Кстати, эта команда также поддерживает нестандартные доменные имена. Для более детальной информации используйте go help remote ( Прим. переводчика на русский язык - у меня сработала не эта команда, а go help importpath ).

Эта команда компилирует все пакеты, создает соответствующие файлы и перемещает их в $GOPATH/pkg или $GOPATH/bin .

Эта команда загружает все файлы, чьи имена включают в себя *_test.go и создает исполняемые файлы тестов, а затем печатает на экране информацию, подобную следующей:

По умолчанию она обрабатывает все файлы тестов. Для более подробной информации используйте go help testflag .

Многие говорят, что никакая сторонняя документация для программирования на Go не нужна (вообще, я одну сделал - CHM). В Go существует мощная утилита для того, чтобы управлять документацией "из коробки".

В Go есть больше утилит, чем те, о которых мы только что говорили:

По этим утилитам есть также более детальная информация. Вы можете воспользоваться go help <утилита> , чтобы ознакомиться с ней.

Читайте также: