【问题标题】:Is it possible to make a bash shell script interact with another command line program?是否可以使 bash shell 脚本与另一个命令行程序交互?
【发布时间】:2011-03-27 18:46:35
【问题描述】:

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

一个典型的循环是:

$prog
$$cmdx
$$<some output>
$$save <filename>
$$cmdy
$$<again, some output>
$$save <filename>
$$q
$<back to bash shell>
  • $ 是 bash 提示符
  • $$ 是程序的提示符
  • qprog 的退出命令
  • prog 是这样的,它将前一个命令的输出附加到 filename

如何使这个过程自动化?我想编写一个可以启动该程序的shell脚本,并循环执行这些步骤,将命令一一输入,然后退出。我希望 save 命令能正常工作。

【问题讨论】:

    标签: linux bash shell


    【解决方案1】:

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

    例子:

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

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

    【讨论】:

    • 这符合我的目的。我不确定标准输出会去哪里。
    • 这在某些情况下不起作用...这不是最佳答案...
    【解决方案2】:

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

    【讨论】:

    • 我同意...这是迄今为止最好的工具。
    【解决方案3】:

    哪里有需要,哪里就有办法!我认为这是一个很好的 bash 课程 流程管理和 IPC 是如何工作的。最好的解决方案当然是 Expect。 但真正的原因是管道可能很棘手,并且设计了许多命令 等待数据,这意味着该进程将成为僵尸,原因如下: 海湾很难预测。但是学习如何以及为什么会提醒我们什么是 在引擎盖下进行。

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

    关键是我们想与另一个进程交谈。这是一个服务器:

    # 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
    

    现在,希望能发挥作用的脚本。

    # 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
    

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

        【解决方案5】:

        对于简单的用例,您可以结合使用 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 
        

        【讨论】:

          【解决方案6】:
          echo "cmdx\nsave\n...etc..." | prog
          

          ..?

          【讨论】:

          • 它的行为方式因 shell 不同而不同 -- 请参阅 pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html,尤其是应用程序部分:不可能在所有 POSIX 系统中以可移植方式使用 echo,除非两者都 -n(如第一个参数)和转义序列被省略\n就是这样一个转义序列);以及以后:鼓励新应用程序使用 printf 而不是 echo。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-11-04
          • 1970-01-01
          • 2012-07-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多