My application works with all kind of shell commands, provided from the console (curl
, date
, ping
, whatever). Now I'd like to cover the case with interactive shell commands (like mongo shell), using os/exec
.
e.g. as a first step, connect to mongodb:
mongo --quiet --host=localhost blog
then perform arbitrary number of commands, getting the result on every step
db.getCollection('posts').find({status:'INACTIVE'})
and then
exit
I tried the following, but it allows me to perform only one command per mongo connection:
func main() {
cmd := exec.Command("sh", "-c", "mongo --quiet --host=localhost blog")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
stdin, _ := cmd.StdinPipe()
go func() {
defer stdin.Close()
io.WriteString(stdin, "db.getCollection('posts').find({status:'INACTIVE'}).itcount()")
// fails, if I'll do one more here
}()
cmd.Run()
cmd.Wait()
}
Is there a way to run multiple commands, getting stdout result per executed command?
As Flimzy noted, you should absolutely be using a mongo driver to work with mongo, not trying to interact with it via shell exec.
However, to answer the root question, of course you can execute multiple commands - there's no reason you can't. Every time you write to the process' stdin, it's like you're at a terminal typing into it. There's no secret limitation on that, other than processes which specifically detect if they're connected to a TTY.
Your code has several issues, though - you should definitely review the os/exec
package documentation. You're calling cmd.Run
, which:
starts the specified command and waits for it to complete.
And then calling cmd.Wait
, which... also waits for the command to complete. You're writing to the stdin pipe in a goroutine, even though this is a very serialized process: you want to write to the pipe to execute a command, get the result, write another command, get another result... concurrency only muddles matters and should not be used here. And you're not sending newlines to tell Mongo you're done writing a command (just like you'd do in the shell - Mongo won't just start executing as soon as you enter the closing paren, you have to hit enter).
What you would want to do to interact with a process via stdin/stdout (again, noting that this is absolutely not the way to interact with a database, but could be valid for other external commands):
cmd := exec.Command("sh", "-c", "mongo --quiet --host=localhost blog")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
stdin, _ := cmd.StdinPipe()
// Start command but don't wait for it to exit (yet) so we can interact with it
cmd.Start()
// Newlines, like hitting enter in a terminal, tell Mongo you're done writing a command
io.WriteString(stdin, "db.getCollection('posts').find({status:'INACTIVE'}).itcount()\n")
io.WriteString(stdin, "db.getCollection('posts').find({status:'ACTIVE'}).itcount()\n")
// Quit tells it you're done interacting with it, otherwise it won't exit
io.WriteString(stdin, "quit()\n")
stdin.Close()
// Lastly, wait for the process to exit
cmd.Wait()