3

所以我有一个 bash 命令来启动服务器,它会在输出一些行之前输出一些内容,例如“服务器已启动,按 Control+C 退出”。如何通过管道输出此输出,因此当此行发生时,我将此进程置于后台并继续执行另一个脚本/函数(即执行需要等到服务器启动的事情,例如运行测试)

我想结束 3 个功能

  • 启动服务器
  • 运行测试
  • 停止服务器

我有一些类似的东西:

function read_server_output{
    while read data; do
        printf "$data"
        if [[ $data == "Server started, Press Control+C to exit" ]]; then
            # do something here to put server process in the background
            # so I can run another function
        fi
    done
}

function start_server{
    # start the server and pipe its output to another function to check its running
    start-server-command | read_server_output
}

function run_test{
    # do some stuff
}

function stop_server{
    # stop the server
}

# run the bash script code
start_server()
run_tests()
stop_tests()

相关问题可能是SH/BASH - 扫描日志文件,直到出现一些文本,然后退出。如何?

在此先感谢我对此很陌生。

4

2 回答 2

4

首先,关于术语的注释...

“背景”和“前景”是控制终端的概念,也就是说,它们与您键入ctrl+C,ctrl+Z等时发生的情况有关(哪个进程获取信号),进程是否可以从终端设备读取(“背景” " 进程得到一个SIGTTIN默认情况下会导致它停止的),依此类推。

很明显,这与您想要实现的目标无关。相反,你有一个行为不端的程序(或程序套件)需要一些特殊的溺爱:当服务器第一次启动时,它需要一些手牵手到某个点,之后就可以了。一旦输出一些文本字符串,手持就可以停止(请参阅您的相关问题或下面的技术)。

这里有一个很大的潜在问题:很多程序,当它们的输出被重定向到管道或文件时,直到它们打印出一个“块”的输出值,或者正在退出时才产生输出如果是这种情况,一个简单的:

start-server-command | cat

不会打印您要查找的行(因此这是判断您是否也必须解决此问题的一种快速方法)。如果是这样,您将需要类似expect的东西,这是实现您想要的完全不同的方式。

不过,假设这不是问题,让我们尝试一种完全在 shell 中的方法。

您需要运行start-server-command并保存进程 ID,以便您可以(最终)向其发送SIGINT信号(就像ctrl+C进程“前台”一样,但您是从脚本执行此操作,而不是从控制终端,因此脚本无法按下任何键)。幸运的是sh,有一个专门用于此的语法。

首先让我们创建一个临时文件:

#! /bin/sh
# myscript - script to run server, check for startup, then run tests

TMPFILE=$(mktemp -t myscript) || exit 1    # create /tmp/myscript.<unique>
trap "rm -f $TMPFILE" 0 1 2 3 15           # arrange to clean up when done

现在启动服务器并保存它的 PID:

start-server-command > $TMPFILE &          # start server, save output in file
SERVER_PID=$!                              # and save its PID so we can end it

trap "kill -INT $SERVER_PID; rm -f $TMPFILE" 0 1 2 3 15 # adjust cleanup

现在您需要扫描$TMPFILE直到出现所需的输出,就像在另一个问题中一样。因为这需要一定数量的轮询,所以您应该插入延迟。检查服务器是否失败并终止而没有到达“开始”点也可能是明智的。

while ! grep '^Server started, Press Control+C to exit$' >/dev/null; do
    # message has not yet appeared, is server still starting?
    if kill -0 $SERVER_PID 2>/dev/null; then
        # server is running; let's wait a bit and try grepping again
        sleep 1 # or other delay interval
    else
        echo "ERROR: server terminated without starting properly" 1>&2
        exit 1
    fi
done

(这里kill -0用来测试进程是否还存在;如果不存在,则退出。“清理”kill -INT会产生错误消息,但可能没问题。如果不存在,要么重定向该kill命令的错误输出,要么调整清理或手动执行,如下所示。)

此时,服务器正在运行,您可以进行测试。当您希望它像用户点击一样退出时ctrl+C,请发送一个SIGINTwith kill -INT

因为kill -INTtrap脚本退出 (0) 以及脚本被SIGHUP(1)、SIGINT(2)、SIGQUIT(3) 和SIGTERM(15) 终止时的集合中有一个 - 这就是:

trap "do some stuff" 0 1 2 3 15

部分——此时你可以简单地让你的脚本退出,除非你也想专门等待服务器退出。如果你想要,也许:

kill -INT $SERVER_PID; rm -f $TMPFILE   # do the pre-arranged cleanup now
trap - 0 1 2 3 15                       # don't need it arranged anymore
wait $SERVER_PID                        # wait for server to finish exit

将是适当的。

(显然以上都没有经过测试,但这是一般框架。)

于 2013-10-23T23:57:19.780 回答
3

可能最简单的做法是在后台启动它并阻止读取它的输出。就像是:

{ start-server-command & } | {
  while read -r line; do
    echo "$line"
    echo "$line" | grep -q 'Server started' && break
  done
  cat &
}
echo script continues here after server outputs 'Server started' message

但这是一个非常丑陋的黑客。如果可以修改服务器以执行脚本可以等待的更具体的操作,那就更好了。

于 2013-10-23T23:52:53.610 回答