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. Сравните две строки:
main.go— 119 байт текста, права-rw-: можно читать и писать.hello— около двух с половиной мегабайт, в правах естьx: файл можно запускать (execute).
Числа у вас будут другие: размер собранного файла зависит от пути сборки и версии 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 ./hello2. Теперь временно замените в
main.goточку в конце второй строки на восклицательный знак и сохраните. Запустите:▶ Выполните./hello go run .
./helloнапечатает старую строку с точкой,go run .— новую со знаком.3. Пересоберите и проверьте ещё раз:
▶ Выполнитеgo build ./hello4. Верните точку и пересоберите — проверка дня ждёт
Я пишу на Go.с точкой, и файлhelloв папке должен остаться.Готово, когда: в
main.goснова точка, а./helloпосле новой сборки печатаетЯ пишу на Go.с точкой (пункты проверкиt_build,hello_bin,t_exec).
Дальше: как печатать то, что нужно, — course next