是否可以使 bash shell 脚本与另一个命令行程序交互?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3448468/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 19:34:09  来源:igfitidea点击:

Is it possible to make a bash shell script interact with another command line program?

linuxbashshell

提问by rup

I am using a interactive command line program in a Linux terminal running the bash shell. I have a definite sequence of command that I input to the shell program. The program writes its output to standard output. One of these commands is a 'save' command, that writes the output of the previous command that was run, to a file to disk.

我在运行 bash shell 的 Linux 终端中使用交互式命令行程序。我有一个明确的命令序列,可以输入到 shell 程序中。程序将其输出写入标准输出。其中一个命令是“save”命令,它将先前运行的命令的输出写入到磁盘的文件中。

A typical cycle is:

一个典型的循环是:

$prog
$$cmdx
$$<some output>
$$save <filename>
$$cmdy
$$<again, some output>
$$save <filename>
$$q
$<back to bash shell>
  • $is the bash prompt
  • $$is the program's prompt
  • qis the quit command for prog
  • progis such that it appends the output of the previous command to filename
  • $是 bash 提示符
  • $$是程序的提示
  • qprog的退出命令
  • prog是这样的,它将前一个命令的输出附加到文件名

How can I automate this process? I would like to write a shell script that can start this program, and cycle through the steps, feeding it the commands one by one and, and then quitting. I hope the savecommand works correctly.

我怎样才能自动化这个过程?我想写一个可以启动这个程序的shell脚本,循环执行步骤,一一输入命令,然后退出。我希望保存命令正常工作。

回答by Tristan

If your command doesn't care how fast you give it input, and you don't really need to interactwith it, then you can use a heredoc.

如果你的命令不关心你输入的速度有多快,而且你真的不需要与它交互,那么你可以使用 heredoc。

Example:

例子:

#!/bin/bash
prog <<EOD
cmdx
save filex
cmdy
save filey
q
EOD

If you need branching based on the output of the program, or if your program is at all sensitive to the timing of your commands, then Expect is what you want.

如果您需要根据程序的输出进行分支,或者如果您的程序对命令的时间很敏感,那么 Expect 就是您想要的。

回答by schot

I recommend you use Expect. This tool is designed to automate interactive shell applications.

我建议您使用Expect。该工具旨在自动化交互式 shell 应用程序。

回答by Mikehat

Where there's a need, there's a way! I think that it's a good bash lesson to see how process management and ipc works. The best solution is, of course, Expect. But the real reason is that pipes can be tricky and many commands are designed to wait for data, meaning that the process will become a zombie for reasons that bay be difficult to predict. But learning how and why reminds us of what is going on under the hood.

有需要,就有办法!我认为了解进程管理和 ipc 的工作原理是很好的 bash 课程。最好的解决方案当然是Expect。但真正的原因是管道可能很棘手,而且许多命令旨在等待数据,这意味着由于难以预测的原因,进程将变成僵尸。但是了解如何以及为什么会提醒我们幕后发生的事情。

When two processes engage in a conversation, the danger is that one or both will try to read data that will never arrive. The rules of engagement have to be crystal clear. Things like CRLF and character encoding can kill the party. Luckily, two close partners like a bash script and its child process are relatively easy to keep in line. The easiest thing to miss is that bash is launching a child process for just about every thing it does. If you can make it work with bash, you thoroughly know what you're doing.

当两个进程进行对话时,危险在于其中一个或两个进程会尝试读取永远不会到达的数据。交战规则必须非常明确。诸如 CRLF 和字符编码之类的东西可以杀死派对。幸运的是,像 bash 脚本及其子进程这样的两个亲密伙伴相对容易保持一致。最容易错过的是 bash 正在为它所做的每一件事启动一个子进程。如果你能用 bash 使它工作,你就完全知道你在做什么。

The point is that we want to talk to another process. Here's a server:

关键是我们想和另一个进程对话。这是一个服务器:

# a really bad SMTP server

# a hint at courtesy to the client
shopt -s nocasematch

echo "220 $HOSTNAME SMTP [$$]"

while true
do
    read
    [[ "$REPLY" =~ ^helo\ [^\ ] ]] && break
    [[ "$REPLY" =~ ^quit ]] && echo "Later" && exit
    echo 503 5.5.1 Nice guys say hello.
done

NAME=`echo "$REPLY" | sed -r -e 's/^helo //i'`
echo 250 Hello there, $NAME 

while read
do
    [[ "$REPLY" =~ ^mail\ from: ]] && { echo 250 2.1.0 Good guess...; continue; }
    [[ "$REPLY" =~ ^rcpt\ to: ]] && { echo 250 2.1.0 Keep trying...; continue; }
    [[ "$REPLY" =~ ^quit ]] && { echo Later, $NAME; exit; }
    echo 502 5.5.2 Please just QUIT
done

echo Pipe closed, exiting

Now, the script that hopefully does the magic.

现在,脚本有望发挥魔力。

# Talk to a subprocess using named pipes

rm -fr A B      # don't use old pipes
mkfifo A B

# server will listen to A and send to B
./smtp.sh < A > B &

# If we write to A, the pipe will be closed.
# That doesn't happen when writing to a file handle.
exec 3>A

read < B
echo "$REPLY"

# send an email, so long as response codes look good
while read L
do
    echo "> $L"
    echo $L > A
    read < B
    echo $REPLY
    [[ "$REPLY" =~ ^2 ]] || break

done <<EOF
HELO me
MAIL FROM: me
RCPT TO: you
DATA
Subject: Nothing

Message
.
EOF

# This is tricky, and the reason sane people use Expect.  If we
# send QUIT and then wait on B (ie. cat B) we may have trouble.
# If the server exits, the "Later" response in the pipe might
# disappear, leaving the cat command (and us) waiting for data.
# So, let cat have our STDOUT and move on.
cat B &

# Now, we should wait for the cat process to get going before we
# send the QUIT command. If we don't, the server will exit, the
# pipe will empty and cat will miss its chance to show the
# server's final words.
echo -n > B     # also, 'sleep 1' will probably work.

echo "> quit"
echo "quit" > A

# close the file handle
exec 3>&-

rm A B

Notice that we are not simply dumping the SMTP commands on the server. We check each response code to make sure things are OK. In this case, things will not be OK and the script will bail.

请注意,我们不是简单地将 SMTP 命令转储到服务器上。我们检查每个响应代码以确保一切正常。在这种情况下,事情不会好起来,脚本会保释。

回答by speeds images

I use Expect to interact with the shell for switch and router backups. A bash script calls the expect script with the correct variables.

我使用 Expect 与外壳交互以进行交换机和路由器备份。bash 脚本使用正确的变量调用 expect 脚本。

for i in <list of machines> ; do expect_script.sh $i ; exit

This will ssh to each box, run the backup commands, copy out the appropriate files, and then move on to the next box.

这将 ssh 到每个框,运行备份命令,复制出适当的文件,然后移动到下一个框。

回答by yabt

For simple use cases you may use a combination of subshell, echo & sleep:

对于简单的用例,您可以结合使用 subshel​​l、echo 和 sleep:

# in Terminal.app
telnet localhost 25
helo localhost
ehlo localhost
quit

(sleep 5; echo "helo localhost"; sleep 5; echo "ehlo localhost"; sleep 5; echo quit ) | 
   telnet localhost 25 

回答by user376258

echo "cmdx\nsave\n...etc..." | prog

..?

..?