Golang Command-Line String Flag - go

I'm trying to get command line flags working with strings in golang. Here is my main/main.go file:
package main
import (
"flag"
"log"
)
func main() {
flagString := flag.String("string", "foo", "Enter this string and have it printed back out.")
log.Println("You entered ", *flagString)
}
This simply takes the flag from the command line and prints it out, with default value "foo".
I enter the following into the command prompt after building the project, trying to make it print out bar:
> main -string=bar
(log time+date) You entered foo
> main -string="bar"
(log time+date) You entered foo
Is there something wrong with my code or am I entering it into the command prompt incorrectly?
By the way, I am running Windows 10.

After calling flag.String(...), you need to simply call flag.Parse().
In your example:
package main
import (
"flag"
"log"
)
func main() {
flagString := flag.String("string", "foo", "Enter this string and have it printed back out.")
flags.Parse()
log.Println("You entered ", *flagString)
}

Related

Golang: Command Prompt

I am writing a personal tool which gives me a prompt and lets me execute useful commands. Like bash but its not a shell program
I want an input prompt like Bash
If you enter nothing, just print a new line and scan for input again
If you enter the hotkey Ctrl + D, terminate the program
It should be like this
[user#computer ~]$ go build
[user#computer ~]$ ./myapp
$
$ command
.. do command
$ (Ctrl + D hotkey)
[user#computer ~]$
I tried using Scanln but it didn't accept spaces which was a huge problem
EDIT: here is what I implemented
func main() {
var input string
fmt.Println(banners.Gecko1)
fmt.Print("$ ")
fmt.Scanln(&input)
}
Here is the console:
[user#pc MyTool]$ go build
[user#pc MyTool]$ ./MyTool
-----ENTER COMMANDS
$ term1 term2 term3 term4 term5
[user#pc MyTool]$ erm2 term3 term4 term5
bash: erm2: command not found
[user#pc MyTool]$
as you can see, everything after term1 is ignored and somehow passed to bash...
I think that's how scanln is supposed to work. The docs state that it "scans text from standard input, storing successive space-separated values into successive arguments." Alternatively, use the bufio package if you want to read one line at a time. You can refer to the sample code below:
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
input_reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("$ ")
line, _ := input_reader.ReadString('\n')
line = strings.Replace(line, "\n", "", -1)
fmt.Println(line)
}
}

Go Printing in VSCode

A bit confused on why the following code does not print anything in vscode using the go extension (and properly installed Go)
import "fmt"
func main() {
fmt.Println("Hello World!")
}
Nothing at all gets outputted.
If I remove the exclamation mark within in the print statement
It prints out Hello World
Maybe this:
package main
import "fmt"
func main() {
fmt.Println("Hello World!");
}
→ go build main.go
→ ./main
Hello World!
Don't forget the semiocolon at the end of go line and instruction package follow by name of package at the beginning of the file.

How do I iterate through command line arguments and collect what's left over after the flags?

My goal is for "init", "init -site=test", both versions of init and also the standalone "debug" command to be accepted at the command line, and to treat anything left over as a filename.
What actually happens is that in the case of "init -site=test" for some reason the "-site=test" is also accepted as a filename. How can I stop that from happening?
package main
import (
"flag"
"fmt"
"os"
)
func main() {
initCmd := flag.NewFlagSet("init", flag.ExitOnError)
initSiteName := initCmd.String("site", "", "Main name for your site")
flag.Parse()
for pos, cmd := range os.Args {
switch cmd {
case "debug":
fmt.Printf("debug\n")
case "init":
initCmd.Parse(os.Args[pos+1:])
fmt.Printf("init\n site name:%v\n", *initSiteName)
default:
fmt.Printf("Filename: %v\n", cmd);
}
}
}
It's not very convenient using the flag package. From the doc:
Flag parsing stops just before the first non-flag argument ("-" is a non-flag argument) or after the terminator "--".
You would have to do it manually:
After parsing, the arguments following the flags are available as the slice flag.Args() or individually as flag.Arg(i).
Or you can use another package.

Go fmt.Scanln that doesn't echo back characters typed by user. For password

How do I capture user input from the command line without echoing back the characters that the user types. I want to use this to capture a password. Like getpass.getpass in Python.
package main
import (
"fmt"
)
func main() {
var password string
fmt.Scanln(&password)
}
There is no helper function in the standard library for this.
You have to create your own, or use an existing one like gopass (supports windows, unix, bsd).
Using gopass: (example taken from their website)
import "fmt"
import "github.com/howeyc/gopass"
func main() {
fmt.Printf("Password: ")
pass := gopass.GetPasswd() // Silent, for *'s use gopass.GetPasswdMasked()
// Do something with pass
}

Go: embed bash script in binary

I'm trying to cross compile a Go program that will execute a bash script. Is it possible to embed the bash script in the binary?
I've referenced:
Golang serve static files from memory
Not sure if this applies to executing bash scripts though. Am I missing something here? Some clarification or pointers will be very helpful, thanks!
Since bash can execute scripts from stdin, you just send your script to the bash command via command.Stdin.
An example without go embed:
package main
import (
"bytes"
"fmt"
"os/exec"
"strings"
)
var script = `
echo $PWD
pwd
echo "-----------------------------------"
# print process
ps aux | grep code
`
func main() {
c := exec.Command("bash")
c.Stdin = strings.NewReader(script)
b, e := c.Output()
if e != nil {
fmt.Println(e)
}
fmt.Println(string(b))
}
With go 1.16 embed (https://golang.org/pkg/embed/):
package main
import (
"bytes"
_ "embed"
"fmt"
"os/exec"
"strings"
)
//go:embed script.sh
var script string
func main() {
c := exec.Command("bash")
c.Stdin = strings.NewReader(script)
b, e := c.Output()
if e != nil {
fmt.Println(e)
}
fmt.Println(string(b))
}
Bonus
Passing parameters to your script with -s -.
The following example will pass -la /etc to the script.
package main
import (
"fmt"
"os/exec"
"strings"
)
func main() {
// pass parameters to your script as a safe way.
c := exec.Command("sh", "-s", "-", "-la", "/etc")
// use $1, $2, ... $# as usual
c.Stdin = strings.NewReader(`
echo $#
ls $1 "$2"
`)
b, e := c.Output()
if e != nil {
fmt.Println(e)
}
fmt.Println(string(b))
}
Playground: https://go.dev/play/p/T1lMSrXcOIL
You can actually directly interface with the system shell in Go. Depending on what's in your bash script you can probably convert everything completely to go. For example things like handling files, extracting archives, outputting text, asking for user input, downloading files, and so much more can be done natively in Go. For anything you absolutely need the shell for you can always use golang.org/pkg/os/exec.
I wrote a snippet that demonstrates a really simple Go based command shell. Basically it pipes input, output, and error between the user and the shell. It can be used interactively or to directly run most shell commands. I'm mentioning it here mostly to demonstrate Go's OS capabilities. Check it out: github.com/lee8oi/goshell.go
Did you try writing the stream-data (according to the reference go-bindata provides a function that returns []byte) into a temporary file?
see: http://golang.org/pkg/io/ioutil/#TempFile
you can then execute it with a syscall
http://golang.org/pkg/syscall/#Exec
where the first argument needs to be a shell.

Resources