День 01 · Программа, терминал, первый запуск · страница 4 из 6

go run и go build

На первой странице был путь «исходный код → компилятор → исполняемый файл → запуск». go run проходит его целиком за одну команду и прячет середину. Сейчас разберём середину — пригодится, когда программа начнёт работать на сервере, где нет ни исходников, ни компилятора.


go run — собрать и сразу запустить

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

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day01/hello$ go run .
Привет, мир!
Я пишу на Go.

Как это устроено. go run компилирует программу во временный файл где-то в системной папке, запускает его и после завершения удаляет. В папке hello после go run ничего не появляется:

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day01/hello$ ls
main.go

Когда применять. Пока пишете и проверяете: поправили — go run . — посмотрели — поправили снова.


go build — собрать исполняемый файл

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

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day01/hello$ go build
stagiaire@lab:~/gocourse/day01/hello$ ls -l
total 2292
-rwxrwxr-x 1 stagiaire stagiaire 2341913 Sep 29 02:36 hello
-rw-rw-r-- 1 stagiaire stagiaire     119 Sep 29 02:36 main.go

Что увидите. go build при успехе молчит. В папке появился файл hello — имя он получил по названию папки.

Как читать ls -l. Сравните две строки:

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

Почему такой большой для программы из двух строк? Внутри не только ваши команды, но и всё, что нужно для работы любой Go-программы: управление памятью, работа с системой. Зато файл самодостаточный — ему не нужен установленный Go.


Попробуйте сейчас: соберите файл и посмотрите на него.

Цель: увидеть, что после go build в папке лежит второй файл — в тысячи раз больше исходника и с правом на запуск.

▶ Выполните
cd ~/gocourse/day01/hello
go build
ls -l

Готово, когда: ls -l показывает два файла, у hello в правах есть x, и он в тысячи раз больше (пункт проверки t_build).


Запуск собранного файла: ./

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day01/hello$ ./hello
Привет, мир!
Я пишу на Go.

Почему ./hello, а не hello. Когда вы печатаете имя команды — ls, go — терминал ищет программу с таким именем в нескольких системных папках. Текущей папки в этом списке нет намеренно, чтобы случайный файл с именем ls не подменил настоящую команду. ./ — это явное «из текущей папки»: точка — текущая папка, косая черта отделяет имя файла.

Без ./:

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day01/hello$ hello
Command 'hello' not found, but can be installed with:
sudo snap install hello  # version 2.10, or
sudo apt  install hello  # version 2.10-5build1
See 'snap info hello' for additional versions.

Файл лежит рядом, но терминал искал его не здесь. Остальные строки — совет Ubuntu: в её каталоге программ есть чужая программа с тем же именем hello. Ничего не устанавливайте. Если поставить её, команда hello начнёт запускать не вашу программу, а ту, и будет печатать Hello, world!. Ваша запускается так: ./hello.


Собранный файл не знает про исходник

Ситуация. Вы поменяли текст в main.go, сохранили, запустили ./hello — а печатается старое.

Так и должно быть. Исполняемый файл — снимок программы на момент сборки. Правки в main.go попадут в него только после нового go build. go run этой ловушки не имеет: он собирает заново при каждом запуске.

Собранный файл работает и без исходника, из любой папки:

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day01/hello$ cp hello /tmp/hello-copy
stagiaire@lab:~/gocourse/day01/hello$ cd /tmp
stagiaire@lab:/tmp$ ./hello-copy
Привет, мир!
Я пишу на Go.

cp копирует файл: первое — что, второе — куда. В /tmp нет ни main.go, ни go.mod, а программа работает. Именно так Go-сервисы и попадают на серверы: собрали один файл, скопировали, запустили.


Своё имя файла: -o

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day01/hello$ go build -o greeter
stagiaire@lab:~/gocourse/day01/hello$ ./greeter
Привет, мир!
Я пишу на Go.

Ключ -o (output) задаёт имя результата.


Итого

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

Что может пойти не так

Что видите Что это значит Что делать
Command 'hello' not found, but can be installed with: и советы sudo snap install hello, sudo apt install hello запускаете без ./; Ubuntu предлагает чужую программу с тем же именем ничего не устанавливать; ./hello
bash: ./hello: No such file or directory файла нет: не собирали или вы в другой папке ls; если файла нет — go build
./hello печатает старый текст собранный файл старше правок go build, потом ./hello
go build ничего не напечатал это успех ls -l покажет новый файл
go build напечатал ошибку, файла нет в тексте программы ошибка — компилятор не собирает ничего читать ошибку: об этом шестая страница

Попробуйте сейчас: поймайте ловушку старого снимка.

Цель: увидеть, что ./hello запускает снимок на момент сборки, а go run . — свежий текст, и оставить в папке свежесобранный hello.

1. Запустите собранный файл:

▶ Выполните
cd ~/gocourse/day01/hello
./hello

2. Теперь временно замените в main.go точку в конце второй строки на восклицательный знак и сохраните. Запустите:

▶ Выполните
./hello
go run .

./hello напечатает старую строку с точкой, go run . — новую со знаком.

3. Пересоберите и проверьте ещё раз:

▶ Выполните
go build
./hello

4. Верните точку и пересоберите — проверка дня ждёт Я пишу на Go. с точкой, и файл hello в папке должен остаться.

Готово, когда: в main.go снова точка, а ./hello после новой сборки печатает Я пишу на Go. с точкой (пункты проверки t_build, hello_bin, t_exec).


Дальше: как печатать то, что нужно, — course next