├── LICENSE ├── README.md ├── go.mod ├── main.go └── main_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013 Dmitri Shuralyov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | goexec 2 | ====== 3 | 4 | [![Go Reference](https://pkg.go.dev/badge/github.com/shurcooL/goexec.svg)](https://pkg.go.dev/github.com/shurcooL/goexec) 5 | 6 | goexec is a command line tool to execute Go code. Output is printed as goons to stdout. 7 | 8 | Installation 9 | ------------ 10 | 11 | ```sh 12 | go install github.com/shurcooL/goexec@latest 13 | ``` 14 | 15 | Usage 16 | ----- 17 | 18 | ``` 19 | Usage: goexec [flags] [packages] [package.]function(parameters) 20 | echo parameters | goexec -stdin [flags] [packages] [package.]function 21 | -compiler string 22 | Compiler to use, one of: "gc", "gopherjs". (default "gc") 23 | -n Print the generated source but do not run it. 24 | -quiet 25 | Do not dump the return values as a goon. 26 | -stdin 27 | Read func parameters from stdin instead. 28 | -tags string 29 | A comma-separated list of build tags to consider satisfied during the build. 30 | ``` 31 | 32 | Examples 33 | -------- 34 | 35 | ```sh 36 | $ goexec 'strings.Repeat("Go! ", 5)' 37 | (string)("Go! Go! Go! Go! Go! ") 38 | 39 | $ goexec strings 'Replace("Calling Go functions from the terminal is hard.", "hard", "easy", -1)' 40 | (string)("Calling Go functions from the terminal is easy.") 41 | 42 | # Note that parser.ParseExpr returns 2 values (ast.Expr, error). 43 | $ goexec 'parser.ParseExpr("5 + 7")' 44 | (*ast.BinaryExpr)(&ast.BinaryExpr{ 45 | X: (*ast.BasicLit)(&ast.BasicLit{ 46 | ValuePos: (token.Pos)(1), 47 | Kind: (token.Token)(5), 48 | Value: (string)("5"), 49 | }), 50 | OpPos: (token.Pos)(3), 51 | Op: (token.Token)(12), 52 | Y: (*ast.BasicLit)(&ast.BasicLit{ 53 | ValuePos: (token.Pos)(5), 54 | Kind: (token.Token)(5), 55 | Value: (string)("7"), 56 | }), 57 | }) 58 | (interface{})(nil) 59 | 60 | # Run function RepoRootForImportPath from package "golang.org/x/tools/go/vcs". 61 | $ goexec 'vcs.RepoRootForImportPath("rsc.io/pdf", false)' 62 | (*vcs.RepoRoot)(...) 63 | (interface{})(nil) 64 | 65 | $ goexec -quiet 'fmt.Println("Use -quiet to disable output of goon; useful if you want to print to stdout.")' 66 | Use -quiet to disable output of goon; useful if you want to print to stdout. 67 | 68 | $ echo '"fmt"' | goexec -stdin 'gist4727543.GetForcedUse' 69 | (string)("var _ = fmt.Errorf") 70 | ``` 71 | 72 | Alternatives 73 | ------------ 74 | 75 | - [gommand](https://github.com/sno6/gommand) - Go one liner program, similar to python -c. 76 | - [gorram](https://github.com/natefinch/gorram) - Like go run for any Go function. 77 | - [goeval](https://github.com/dolmen-go/goeval) - Run Go snippets instantly from the command-line. 78 | 79 | License 80 | ------- 81 | 82 | - [MIT License](LICENSE) 83 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/shurcooL/goexec 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // goexec is a command line tool to execute Go code. Output is printed as goons to stdout. 2 | package main 3 | 4 | import ( 5 | "flag" 6 | "fmt" 7 | "go/build" 8 | "io/ioutil" 9 | "log" 10 | "os" 11 | "os/exec" 12 | "path/filepath" 13 | "strconv" 14 | "strings" 15 | 16 | "golang.org/x/tools/imports" 17 | 18 | // We need go-goon to be available; this ensures getting goexec will get go-goon too. 19 | _ "github.com/shurcooL/go-goon" 20 | ) 21 | 22 | var ( 23 | quietFlag = flag.Bool("quiet", false, "Do not dump the return values as a goon.") 24 | stdinFlag = flag.Bool("stdin", false, "Read func parameters from stdin instead.") 25 | nFlag = flag.Bool("n", false, "Print the generated source but do not run it.") 26 | compilerFlag = flag.String("compiler", "gc", `Compiler to use, one of: "gc", "gopherjs".`) 27 | tagsFlag = flag.String("tags", "", "A comma-separated list of build tags to consider satisfied during the build.") 28 | ) 29 | 30 | func usage() { 31 | fmt.Fprintln(os.Stderr, `Usage: goexec [flags] [packages] [package.]function(parameters) 32 | echo parameters | goexec -stdin [flags] [packages] [package.]function`) 33 | flag.PrintDefaults() 34 | } 35 | 36 | func main() { 37 | flag.Usage = usage 38 | flag.Parse() 39 | if flag.NArg() < 1 { 40 | flag.Usage() 41 | os.Exit(2) 42 | } 43 | switch *compilerFlag { 44 | case "gc", "gopherjs": 45 | default: 46 | flag.Usage() 47 | os.Exit(2) 48 | } 49 | 50 | wd, err := os.Getwd() 51 | if err != nil { 52 | log.Fatalln(err) 53 | } 54 | 55 | args := flag.Args() 56 | importPaths := args[:len(args)-1] // All but last. 57 | cmd := args[len(args)-1] // Last one. 58 | if *stdinFlag { 59 | stdin, err := ioutil.ReadAll(os.Stdin) 60 | if err != nil { 61 | log.Fatalln(err) 62 | } 63 | 64 | cmd += "(" + strings.TrimSuffix(string(stdin), "\n") + ")" 65 | } 66 | if !*quietFlag { 67 | cmd = "goon.Dump(" + cmd + ")" 68 | } 69 | 70 | // Generate source code. 71 | src := `package main 72 | 73 | import ( 74 | ` 75 | if !*quietFlag { 76 | src += ` "github.com/shurcooL/go-goon" 77 | ` 78 | } 79 | for _, importPath := range importPaths { 80 | bpkg, err := build.Import(importPath, wd, build.FindOnly) 81 | if err != nil { 82 | log.Fatalln(err) 83 | } 84 | if build.IsLocalImport(bpkg.ImportPath) { 85 | log.Fatalf("local import path %q not supported", bpkg.ImportPath) // TODO: Add support for this when it's a priority. 86 | } 87 | src += ` . ` + strconv.Quote(bpkg.ImportPath) + ` 88 | ` 89 | } 90 | src += `) 91 | 92 | func main() { 93 | ` + cmd + ` 94 | } 95 | ` 96 | 97 | // Run `goimports` on the source code. 98 | { 99 | out, err := imports.Process("gen.go", []byte(src), nil) 100 | if err != nil { 101 | fmt.Fprint(os.Stderr, src) 102 | fmt.Fprintln(os.Stderr, "imports.Process:", err) // Output is like "gen.go:8:18: expected ...". 103 | os.Exit(1) 104 | } 105 | src = string(out) 106 | } 107 | 108 | if *nFlag { 109 | fmt.Print(src) 110 | return 111 | } 112 | 113 | // Run the program. 114 | err = run(src) 115 | if err != nil { 116 | fmt.Fprintln(os.Stderr, "### Error ###") 117 | fmt.Fprintln(os.Stderr, err) 118 | os.Exit(1) 119 | } 120 | } 121 | 122 | func run(src string) error { 123 | // Create a temp folder. 124 | tempDir, err := ioutil.TempDir("", "goexec_") 125 | if err != nil { 126 | return err 127 | } 128 | defer func() { 129 | err := os.RemoveAll(tempDir) 130 | if err != nil { 131 | fmt.Fprintln(os.Stderr, "warning: error removing temp dir:", err) 132 | } 133 | }() 134 | 135 | // Write the source code file. 136 | tempFile := filepath.Join(tempDir, "gen.go") 137 | err = ioutil.WriteFile(tempFile, []byte(src), 0600) 138 | if err != nil { 139 | return err 140 | } 141 | 142 | // Compile and run the program. 143 | var cmd *exec.Cmd 144 | switch *compilerFlag { 145 | case "gc": 146 | cmd = exec.Command("go", "run", "-tags", *tagsFlag, tempFile) 147 | case "gopherjs": 148 | cmd = exec.Command("gopherjs", "run", "--tags", *tagsFlag, tempFile) 149 | } 150 | cmd.Stdin = os.Stdin 151 | cmd.Stdout = os.Stdout 152 | cmd.Stderr = os.Stderr 153 | return cmd.Run() 154 | } 155 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func Example_run() { 4 | const src = `package main 5 | 6 | import "github.com/shurcooL/go-goon" 7 | 8 | func main() { 9 | goon.Dump("goexec's run() is working.") 10 | } 11 | ` 12 | err := run(src) 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | // Output: (string)("goexec's run() is working.") 18 | } 19 | --------------------------------------------------------------------------------