SKILL.md

  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)