3

I am building an ftp wrapper that does some stuff before I spawn, I could easily do it in a shell script but wondering how I could do it in go

While exec.Command works for simple commands.

out, err := exec.Command("ls").Output() // Works

How do I wrap commands that are interactive e.g., ftp

out, err := exec.Command("ftp").Output()

It just exits. How do I deal with stdin ?

e.g., bash equivalent :

> ./t.sh 
Welcome to myftp 

ftp> open blahblah.com

> cat t.sh 
#!/bin/bash
echo "Welcome to myftp "
#extra commands such as auth/authoriz.. etc.,
shift
echo "$@"
ftp

c++ equivalent :

int main() {
    system("ftp");
    return 0;
}
Victor
  • 427
  • 1
  • 6
  • 19
  • 1
    How are you going to supply the `ftp` command with input? –  Sep 27 '16 at 22:17
  • 1
    The non interactive part is easy right ? just do : if len(os.Args) > 1 { str = cmd + " " + strings.Join(os.Args[1:], " ") } else { str = cmd } – Victor Sep 27 '16 at 22:26
  • @Victor: You can't just concatenate command arguments in a single string unless you're going to have a shell lex them again. What exactly do you want to do with stdin? Are you supplying a stream, or are you trying connect it to a ptty and emulate user input? Show exactly what you're trying to do, what you've tried, and what you expect to happen. – JimB Sep 27 '16 at 22:36
  • @JimB I have added shell script example, How can I do it in golang ? – Victor Sep 27 '16 at 23:04
  • 1
    I think you'll have to use additional commands. It's not like you can keep an interactive prompt running, you issue a command, you read it's output from stdout, you do whatever logic is necessary in the code to prepare the next command and then run it and continue this process either as a list of statements or in a loop until you have no more commands to run. – evanmcdonnal Sep 27 '16 at 23:27
  • Please provide a runnable example via https://play.golang.org/ – Alex Pliutau Sep 28 '16 at 04:44

4 Answers4

3

I would probably do something like this which is more native and doesn't involve an external package

package main
import (
    "os"
    "os/exec"
)

func main() {

    cmd := exec.Command("ls")
    // redirect the output to terminal
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    cmd.Run()

}
reticentroot
  • 3,612
  • 2
  • 22
  • 39
  • i saw this on redit but if you replace the ls with ftp it doesnt prompt me, it just dies. – Victor Sep 28 '16 at 03:27
  • Oh I see. Check out this stack so that you can set stdin http://stackoverflow.com/questions/11321437/golang-how-to-execute-command-which-requires-input-from-user – reticentroot Sep 28 '16 at 03:42
  • Seems to work for interactive but doesnt for no interactive like if you compile it and pass in an argument e.g., cmd := exec.Command("ftp --help") It doesnt output anything. any idea? – Victor Sep 28 '16 at 04:19
  • 2
    @Victor see this article I think this will help tons https://nathanleclaire.com/blog/2014/12/29/shelled-out-commands-in-golang/ – reticentroot Sep 28 '16 at 04:24
1

Traditionally these sorts of interactive scripting exercises are best done with expect. May I suggest checking out a pure Go equivalent?

From the readme:

child, err := gexpect.Spawn("python")
if err != nil { panic(err) }
child.Expect(">>>")
child.SendLine("print 'Hello World'")
child.Interact()
child.Close()
BJ Black
  • 2,483
  • 9
  • 15
  • 1
    Worth mentioning that Gexpect has built-in timeout management and an example for ftp specifically at https://github.com/ThomasRooney/gexpect/blob/master/examples/ftp.go – BJ Black Sep 28 '16 at 01:55
0

To run any commands which will prompt for user input:

cmd := exec.Command("cf", "login")
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin

cmd.Run()
Vidhi Shah
  • 81
  • 1
  • 3
0

A command can be "wrapped" using the following complete script, based on reticentroot's answer and this answer regarding obtaining exec command return codes (go 1.12+):

package main

import (
    "os"
    "os/exec"
)

var (
    bin = "py"
    args = []string{"-3.8"}
)

func main() {
    cmd := exec.Command(bin, append(args, os.Args[1:]...)...)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin
    if err := cmd.Run() ; err != nil {
        if exitError, ok := err.(*exec.ExitError); ok {
            os.Exit(exitError.ExitCode())
        }
    }
}
inkychris
  • 1,179
  • 2
  • 12
  • 18