1---
2name: scripting-with-go
3description: Creates executable Go scripts with shebang-like behavior. Use when the user wants Go scripts, mentions Go scripting, or needs executable .go files. If working in a Go project, do NOT use unless explicitly requested.
4license: GPL-3.0-or-later
5metadata:
6 author: Amolith <amolith@secluded.site>
7---
8
9Create executable Go scripts using a shell trick (not a true shebang). Scripts run directly like `./script.go` with full argument support.
10
11## Basic pattern
12
13First line of any Go script (choose based on your shell):
14
15```go
16// For dash (Debian/Ubuntu default):
17//usr/bin/env go run "$0" "$@"; exit
18
19// For bash/zsh (avoids gopls formatting complaints):
20/**/usr/bin/env go run "$0" "$@"; exit;
21```
22
23Then `chmod +x script.go` and run `./script.go args`.
24
25**Shell compatibility:**
26- `/**/` syntax works in bash/zsh but NOT in dash
27- `//` syntax works in dash but gopls will complain about formatting
28- Check your `/bin/sh`: `ls -l /bin/sh`
29- Use `//usr/bin/env go` to find go in PATH
30- Use `//usr/local/go/bin/go` for absolute path
31
32## How it works
33
341. OS tries to execute `./script.go` as binary
352. Fails with ENOEXEC (not a binary)
363. OS falls back to `/bin/sh`
374. Shell runs first line: compiles and executes the script
385. `$0` = script path, `$@` = all arguments
396. `exit` (or `exit;`) prevents shell from interpreting remaining Go code
407. Shell normalizes `//path` to `/path` or `/**/path` to `/path`
41
42## Complete example
43
44```go
45//usr/bin/env go run "$0" "$@"; exit
46package main
47
48import (
49 "fmt"
50 "os"
51)
52
53func main() {
54 if len(os.Args) < 2 {
55 fmt.Println("Usage: ./script.go <name>")
56 os.Exit(1)
57 }
58 fmt.Printf("Hello, %s!\n", os.Args[1])
59}
60```
61
62## When to use
63
64**Good for:**
65- Long-lived automation needing stability (Go 1.x compatibility guarantee)
66- Cross-platform scripts
67- Corporate environments
68- Scripts that may grow into programs
69- Avoiding dependency management hell
70
71**Avoid for:**
72- Simple one-liners
73- Systems without Go
74- Throwaway scripts
75
76## Common patterns
77
78### CLI flags
79
80```go
81//usr/bin/env go run "$0" "$@"; exit
82package main
83
84import (
85 "flag"
86 "fmt"
87)
88
89func main() {
90 verbose := flag.Bool("v", false, "verbose output")
91 flag.Parse()
92 fmt.Printf("Verbose: %v, Args: %v\n", *verbose, flag.Args())
93}
94```
95
96### stdin
97
98```go
99//usr/bin/env go run "$0" "$@"; exit
100package main
101
102import (
103 "bufio"
104 "fmt"
105 "os"
106)
107
108func main() {
109 scanner := bufio.NewScanner(os.Stdin)
110 for scanner.Scan() {
111 fmt.Println("Got:", scanner.Text())
112 }
113}
114```
115
116### File operations
117
118```go
119//usr/bin/env go run "$0" "$@"; exit
120package main
121
122import (
123 "fmt"
124 "os"
125 "path/filepath"
126)
127
128func main() {
129 filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
130 if err != nil {
131 return err
132 }
133 if !info.IsDir() {
134 fmt.Println(path)
135 }
136 return nil
137 })
138}
139```
140
141## Notes
142
143- Using `env` finds `go` in PATH, making scripts more portable
144- Semicolon required in `exit;` with `/**/` syntax (but not with `//`)
145- Avoid dependencies for maximum compatibility
146- Slower startup than interpreted languages (compilation time)