I want to replicate the following subprocess.run function call in Golang. What would be the correct way to do it?
subprocess.run(['kinit', username], input=password.encode())
So far, I've figured out how to use exec.Command to run external commands, but what's confusing me is passing strings as input to STDIN of that command. Python's subprocess.run has a handy input parameter that takes care of this, how can I achieve similar results in Golang?
I figured out how to do it.
package main
import "os/exec"
import "strings"
func main() {
cmd := exec.Command("kinit", username)
cmd.Stdin = strings.NewReader(password)
err := cmd.Run()
}
The Stdin attribute of the Command object is the STDIN pipe, we can set it to a strings.NewReader object containing the input string to achieve the same effect as of the Python snipped mentioned in the question.
Related
I need to use lxd-p2c(https://github.com/lxc/lxd/tree/master/lxd-p2c) in golang code.
I try to pass password to the binary lxd-p2c built by the code above, which uses term.ReadPassword(0)(https://github.com/lxc/lxd/blob/master/lxd-p2c/utils.go#L166) to read password in Linux.
I did some search on the internet and tried following code but they just did not work.
# 1
cmd := exec.Command(command, args...)
cmd.Stdin = strings.NewReader(password)
# 2
cmd := exec.Command(command, args...)
stdin, _ := cmd.StdinPipe()
io.WriteString(stdin, password)
Similar but simple code to test: https://play.golang.org/p/l-9IP1mrhA (code from https://stackoverflow.com/a/32768479/9265302)
build the binary and call it in go.
edit:
No workaround found and I removed term.ReadPassword(0) in the source code.
Checking the error in your playground displays inappropriate ioctl for device.
Searching for the error message I found this thread, which notes that non-terminal input is not supported for terminal.ReadPassword. My guess is that passing Stdin input this way makes it pass the input with a character device instead of with the necessary terminal device like tty or any such, making the read fail. lxd-p2c can't read the password from such an input device.
I would like to know why when I execute the command go run example.go the won't print anything on terminal.
The code below works.
package main
import "fmt"
func main() {
fmt.Println("Hello")
}
Will print Hello.
But when I would like to use the function fmt.Printf when I run the command to execute, appear very quickly the response but is deleted on terminal.
package main
import "fmt"
func main() {
var i int = 2
fmt.Printf("%v %T", i, i) // fmt.Print does not work to
}
You use fmt.Printf with a format that does not end with a newline, so your system dutifully prints out the output without a terminating newline.
Presumably your shell then overwrites the output by sending the cursor to the beginning of the line and printing something. To prevent this from happening, either have your program end its output with a newline, or update your shell's prompt to avoid printing over existing output.
(Side note: it's just Go, not Go Lang. This goes give some issues with searching, common among short-named languages like C and C++.)
When Printf is used, you need to put a \n at the end.
Your program will produce undefined: I.
By replacing I with i it should work https://play.golang.org/p/GxFh-SYePR3 and return 2 int.
I am trying to detect if any command in a pipe sequence has failed before the execution of my application.
I am approaching a solution by reading $PIPESTATUS (bash) or $pipestatus (zsh) environment variables, which should contain an array with the exit codes of each command.
Sadly, I am not being able to retrieve these values using os.Getenv. This is a quick proof of concept:
package main
import (
"os"
"fmt"
)
func main() {
fmt.Printf("$PIPESTATUS is /%v/\n", os.Getenv("PIPESTATUS"))
fmt.Printf("$pipestatus is /%v/\n", os.Getenv("pipestatus"))
}
Which results in the following output:
$ true | false | go run read_pipestatus.go
$PIPESTATUS is //
$pipestatus is //
How can I make this approach work or alternatives ones?
I am trying to patch a file using the below command
patch -p0 < <file_path>
My runCommand syntax is as below:
func runCommand(cmd string, args ...string) error {
ecmd := exec.Command(cmd, args...)
ecmd.Stdout = os.Stdout
ecmd.Stderr = os.Stderr
ecmd.Stdin = os.Stdin
err := ecmd.Run()
return err
}
Now I am passing my patch command as below:
cmd = "patch"
args := []string{"-p0", "<", "/tmp/file"}
err = runCommand(cmd, args...)
But I am seeing the below error:
patch: **** Can't find file '<' : No such file or directory
Can you please let me know what I am missing here?
You're missing this paragraph from the documentation:
Unlike the "system" library call from C and other languages, the
os/exec package intentionally does not invoke the system shell and
does not expand any glob patterns or handle other expansions,
pipelines, or redirections typically done by shells. The package
behaves more like C's "exec" family of functions. To expand glob
patterns, either call the shell directly, taking care to escape any
dangerous input, or use the path/filepath package's Glob function. To
expand environment variables, use package os's ExpandEnv.
The shell is responsible for handling < operations. You can set stdin yourself with the file as input or you can use the shell. To use the shell, try something like:
runCommand("/bin/sh", "patch -p0 < /tmp/file")
Note that this won't work on Windows. Reading the file and writing to stdin yourself is a more easily portable solution.
I'm experimenting with cgo to use C code from golang, but in my little hello-world test, I've ran into something I can't understand or find more information about.
I'm starting with a simple test similar to examples I've found
package main
import (
"fmt"
"unsafe"
)
/*
#import <stdio.h>
#import <stdlib.h>
*/
import "C"
func main() {
go2c := "Printed from C.puts"
var cstr *C.char = C.CString(go2c)
defer C.free(unsafe.Pointer(cstr))
C.puts(cstr)
fmt.Printf("Printed from golang fmt\n")
}
This simple example just echoes strings to stdout from both golang (using fmt.Printf) and raw C (using C.puts) via the basic cgo binding.
When I run this directly in my terminal, I see both lines:
$ ./main
Printed from C.puts
Printed from golang fmt
When I run this but redirect output in any way – pipe to less, shell redirection to a file, etc – I only see golang's output:
./main | cat
Printed from golang fmt
What happens to the C.puts content when piping / redirecting?
Secondary questions: Is this a cgo quirk, or a c standard library quirk I'm not aware of? Is this behaviour documented? How would I go about debugging this on my own (e.g. is there a good/plausible way for me to 'inspect' what FD1 really is in each block?)
Update: If it's relevant, I'm using go version go1.6.2 darwin/amd64.
This is C behavior you're seeing.
Go does not buffer stdout, while in C it is usually buffered. When the C library detects stdout is a tty, it may use line buffering, so the additional \n inserted by puts will cause the output to be displayed.
You need to flush stdout to ensure you get all the output:
go2c := "Printed from C.puts"
var cstr *C.char = C.CString(go2c)
defer C.free(unsafe.Pointer(cstr))
C.puts(cstr)
C.fflush(C.stdout)
fmt.Printf("Printed from golang fmt\n")
See also
Why does printf not flush after the call unless a newline is in the format string?
Is stdout line buffered, unbuffered or indeterminate by default?
The C library buffering is per line, so the first line can be left in the buffer before it is properly flushed (done at exit time in C programs). You can either try to flush stdout, or try adding a trailing \n in the first string. Does it work if you add the \n?