英文:
How to interact with another console application using golang?
问题
我想在自己的Go应用程序中与另一个控制台应用程序进行交互。我不想编写另一个FTP客户端,但我将以FTP应用程序作为示例,因为它应该在任何地方都可用。
请参考以下示例代码:
func main() {
cmd := exec.Command("/bin/bash", "-c", "ftp")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Start()
time.Sleep(2 * time.Second)
io.WriteString(cmd.Stdout, "help\r\n")
time.Sleep(2 * time.Second)
io.WriteString(cmd.Stdout, "exit\r\n")
cmd.Wait()
}
在"cmd.Start()"之后,我可以在控制台中看到"ftp>"。然后,我尝试将"help"写入ftp应用程序。我可以在文本中看到"help",但我没有得到预期的输出。当我使用键盘输入"help"时,我会收到预期的文本。
这个示例的预期工作流程如下:
ftp> help
Commands may be abbreviated. Commands are:
! dir mdelete qc site
$ disconnect mdir sendport size
account exit mget put status
append form mkdir pwd struct
ascii get mls quit system
bell glob mode quote sunique
binary hash modtime recv tenex
bye help mput reget tick
case idle newer rstatus trace
cd image nmap rhelp type
cdup ipany nlist rename user
chmod ipv4 ntrans reset umask
close ipv6 open restart verbose
cr lcd prompt rmdir ?
delete ls passive runique
debug macdef proxy send
ftp> exit
有人有想法如何实现这个吗?这个可能吗?
提前感谢您的反馈。
英文:
I'd like to interact with another console application from my own go application. I don't want to write another FTP client, but I'll take the ftp application in this example as it should be available everywhere.
Take the following example:
func main() {
cmd := exec.Command("/bin/bash", "-c", "ftp")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Start()
time.Sleep(2 * time.Second)
io.WriteString(cmd.Stdout, "help\r\n")
time.Sleep(2 * time.Second)
io.WriteString(cmd.Stdout, "exit\r\n")
cmd.Wait()
}
After the "cmd.Start()" I can see "ftp>" in the console. Then I tried to write "help" into the ftp application. I can see "help" as text, but I do not get the expected output back. When I write "help" with the keyboard, I receive the expected text.
The expected workflow for this example would be the following:
ftp> help
Commands may be abbreviated. Commands are:
! dir mdelete qc site
$ disconnect mdir sendport size
account exit mget put status
append form mkdir pwd struct
ascii get mls quit system
bell glob mode quote sunique
binary hash modtime recv tenex
bye help mput reget tick
case idle newer rstatus trace
cd image nmap rhelp type
cdup ipany nlist rename user
chmod ipv4 ntrans reset umask
close ipv6 open restart verbose
cr lcd prompt rmdir ?
delete ls passive runique
debug macdef proxy send
ftp> exit
Has anyone an idea how I can realize this? Is it possible?
Thanks in advance for your feedback.
答案1
得分: 1
感谢您的回答。我尝试使用您的建议进行改进,并使用管道进行了修改。
现在代码如下所示:
func main() {
cmd := exec.Command("/bin/bash", "-c", "ftp")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
log.Fatal(err)
}
go func() {
defer stdin.Close()
stdinCommand := "status\n"
fmt.Print(time.Now().UTC().Format("15:04:05") + " STDIN: " + stdinCommand)
io.WriteString(stdin, stdinCommand)
time.Sleep(1 * time.Second)
stdinCommand = "help\n"
fmt.Print(time.Now().UTC().Format("15:04:05") + " STDIN: " + stdinCommand)
io.WriteString(stdin, stdinCommand)
time.Sleep(2 * time.Second)
}()
scanner := bufio.NewScanner(stdout)
go func() {
for scanner.Scan() {
fmt.Println(time.Now().UTC().Format("15:04:05") + " STDOUT: " + scanner.Text())
}
}()
scannerError := bufio.NewScanner(stderr)
go func() {
for scannerError.Scan() {
fmt.Println(time.Now().UTC().Format("15:04:05") + " STDERR: " + scanner.Text())
}
}()
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
fmt.Print(time.Now().UTC().Format("15:04:05") + " done")
}
现在的输出如下所示:
15:24:26 STDIN: status
15:24:27 STDIN: help
15:24:29 STDOUT: Not connected.
15:24:29 STDOUT: No proxy connection.
15:24:29 STDOUT: Connecting using address family: any.
15:24:29 STDOUT: Mode: ; Type: ; Form: ; Structure:
15:24:29 STDOUT: Verbose: off; Bell: off; Prompting: on; Globbing: on
15:24:29 STDOUT: Store unique: off; Receive unique: off
15:24:29 STDOUT: Case: off; CR stripping: on
15:24:29 STDOUT: Quote control characters: off
15:24:29 STDOUT: Ntrans: off
15:24:29 STDOUT: Nmap: off
15:24:29 STDOUT: Hash mark printing: off; Use of PORT cmds: on
15:24:29 STDOUT: Tick counter printing: off
15:24:29 STDOUT: Commands may be abbreviated. Commands are:
15:24:29 STDOUT:
15:24:29 STDOUT: ! dir mdelete qc site
15:24:29 STDOUT: $ disconnect mdir sendport size
15:24:29 STDOUT: account exit mget put status
15:24:29 STDOUT: append form mkdir pwd struct
15:24:29 STDOUT: ascii get mls quit system
15:24:29 STDOUT: bell glob mode quote sunique
15:24:29 STDOUT: binary hash modtime recv tenex
15:24:29 STDOUT: bye help mput reget tick
15:24:29 STDOUT: case idle newer rstatus trace
15:24:29 done
您是否有提示,我需要更改什么才能直接在输入后获取“status”的响应,然后发送“help”以获取帮助响应。最后,我想发送“exit”以退出程序。在当前版本中,只要我包含了“defer stdin.Close()”,它就会自动退出。当我删除它时,我就无法获得输出。
英文:
Thanks for your answers. I tried to improve it with your recommendations with piping.
Now it looks like this:
func main() {
cmd := exec.Command("/bin/bash", "-c", "ftp")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
log.Fatal(err)
}
go func() {
defer stdin.Close()
stdinCommand := "status\n"
fmt.Print(time.Now().UTC().Format("15:04:05") + " STDIN: " + stdinCommand)
io.WriteString(stdin, stdinCommand)
time.Sleep(1 * time.Second)
stdinCommand = "help\n"
fmt.Print(time.Now().UTC().Format("15:04:05") + " STDIN: " + stdinCommand)
io.WriteString(stdin, stdinCommand)
time.Sleep(2 * time.Second)
}()
scanner := bufio.NewScanner(stdout)
go func() {
for scanner.Scan() {
fmt.Println(time.Now().UTC().Format("15:04:05") + " STDOUT: " + scanner.Text())
}
}()
scannerError := bufio.NewScanner(stderr)
go func() {
for scannerError.Scan() {
fmt.Println(time.Now().UTC().Format("15:04:05") + " STDERR: " + scanner.Text())
}
}()
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
fmt.Print(time.Now().UTC().Format("15:04:05") + " done")
}
The output now looks like this:
15:24:26 STDIN: status
15:24:27 STDIN: help
15:24:29 STDOUT: Not connected.
15:24:29 STDOUT: No proxy connection.
15:24:29 STDOUT: Connecting using address family: any.
15:24:29 STDOUT: Mode: ; Type: ; Form: ; Structure:
15:24:29 STDOUT: Verbose: off; Bell: off; Prompting: on; Globbing: on
15:24:29 STDOUT: Store unique: off; Receive unique: off
15:24:29 STDOUT: Case: off; CR stripping: on
15:24:29 STDOUT: Quote control characters: off
15:24:29 STDOUT: Ntrans: off
15:24:29 STDOUT: Nmap: off
15:24:29 STDOUT: Hash mark printing: off; Use of PORT cmds: on
15:24:29 STDOUT: Tick counter printing: off
15:24:29 STDOUT: Commands may be abbreviated. Commands are:
15:24:29 STDOUT:
15:24:29 STDOUT: ! dir mdelete qc site
15:24:29 STDOUT: $ disconnect mdir sendport size
15:24:29 STDOUT: account exit mget put status
15:24:29 STDOUT: append form mkdir pwd struct
15:24:29 STDOUT: ascii get mls quit system
15:24:29 STDOUT: bell glob mode quote sunique
15:24:29 STDOUT: binary hash modtime recv tenex
15:24:29 STDOUT: bye help mput reget tick
15:24:29 STDOUT: case idle newer rstatus trace
15:24:29 done
Do you have a hint, what I need to change to get the response for "status" directly behind the input and afterwards send "help" to get the help response. In the end I'd like to send "exit" to exit the program. With the current version it automatically exits as long as I have "defer stdin.Close()" included. When I remove it, I do not get the output.
答案2
得分: 0
你想在你的应用程序中使用管道。你尝试的方式是不行的。你在终端中输入命令的方式与Bash和操作系统处理它们的方式是不同的。
这个例子可能对你有帮助,它是从另一个SO问题中复制过来的(查看问题):
package main
import (
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
c2.Stdin, _ = c1.StdoutPipe()
c2.Stdout = os.Stdout
_ = c2.Start()
_ = c1.Run()
_ = c2.Wait()
}
英文:
You want to have piping in your application. That doesn't happen in the way you are trying. The way you write commands in your terminal is different from the way Bash and then OS treats them.
This example might help you, copied from another SO question (view question):
package main
import (
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
c2.Stdin, _ = c1.StdoutPipe()
c2.Stdout = os.Stdout
_ = c2.Start()
_ = c1.Run()
_ = c2.Wait()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论