---
name: scripting-with-go
description: 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.
license: GPL-3.0-or-later
metadata:
  author: Amolith <amolith@secluded.site>
---

Create executable Go scripts using a shell trick (not a true shebang). Scripts run directly like `./script.go` with full argument support.

## Basic pattern

First line of any Go script (choose based on your shell):

```go
// For dash (Debian/Ubuntu default):
//usr/bin/env go run "$0" "$@"; exit

// For bash/zsh (avoids gopls formatting complaints):
/**/usr/bin/env go run "$0" "$@"; exit;
```

Then `chmod +x script.go` and run `./script.go args`.

**Shell compatibility:**
- `/**/` syntax works in bash/zsh but NOT in dash
- `//` syntax works in dash but gopls will complain about formatting
- Check your `/bin/sh`: `ls -l /bin/sh`
- Use `//usr/bin/env go` to find go in PATH
- Use `//usr/local/go/bin/go` for absolute path

## How it works

1. OS tries to execute `./script.go` as binary
2. Fails with ENOEXEC (not a binary)
3. OS falls back to `/bin/sh`
4. Shell runs first line: compiles and executes the script
5. `$0` = script path, `$@` = all arguments
6. `exit` (or `exit;`) prevents shell from interpreting remaining Go code
7. Shell normalizes `//path` to `/path` or `/**/path` to `/path`

## Complete example

```go
//usr/bin/env go run "$0" "$@"; exit
package main

import (
	"fmt"
	"os"
)

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Usage: ./script.go <name>")
		os.Exit(1)
	}
	fmt.Printf("Hello, %s!\n", os.Args[1])
}
```

## When to use

**Good for:**
- Long-lived automation needing stability (Go 1.x compatibility guarantee)
- Cross-platform scripts
- Corporate environments
- Scripts that may grow into programs
- Avoiding dependency management hell

**Avoid for:**
- Simple one-liners
- Systems without Go
- Throwaway scripts

## Common patterns

### CLI flags

```go
//usr/bin/env go run "$0" "$@"; exit
package main

import (
	"flag"
	"fmt"
)

func main() {
	verbose := flag.Bool("v", false, "verbose output")
	flag.Parse()
	fmt.Printf("Verbose: %v, Args: %v\n", *verbose, flag.Args())
}
```

### stdin

```go
//usr/bin/env go run "$0" "$@"; exit
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		fmt.Println("Got:", scanner.Text())
	}
}
```

### File operations

```go
//usr/bin/env go run "$0" "$@"; exit
package main

import (
	"fmt"
	"os"
	"path/filepath"
)

func main() {
	filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() {
			fmt.Println(path)
		}
		return nil
	})
}
```

## Notes

- Using `env` finds `go` in PATH, making scripts more portable
- Semicolon required in `exit;` with `/**/` syntax (but not with `//`)
- Avoid dependencies for maximum compatibility
- Slower startup than interpreted languages (compilation time)
