dpv46227 2019-05-30 05:23
浏览 53
已采纳

Bash脚本在Go程序中运行时挂起

I have a bash script postinstall.sh that executes normally when run from the terminal

./postinstall.sh

the script runs a bunch of commands, creating directorys, chown,chmod files and directorys,

at the end of the script is

echo "Done"
exit 0

When i run this as a exec.Cmd in go, and call the cmd.Wait() funtion, it never returns.

So I modifed my code to use the package from https://github.com/go-cmd/cmd since I wanted to be able to log the output of the scripts I run in 'real time' but also detect when they are complete. So after a bunch of work I can now log the stdOut of the Cmd to my logging at logLevel STATUS and the stdErr of the Cmd to my logLevel ERROR. I can also set a timeout for the command, and log its progress in real time.

This new process works as expected for some simple cases, but when I thow the big postinstall.sh script at it, it runs all the way to the end, and then hangs, and eventually times out. The process takes a few seconds when run manually on the command line, I set the timeout to be 30 seconds. the logging shows the

STATUS- Done

which is the line right before the

exit 0

The docs for the go-cmd package show a non locking way to tell that the process has finished, which I am checking (and works my test cases) I also am checking the cmd.Status().finished flag (bool) and neither of these are reporting that the process has finished. This is the same result I was seeing by just using the cmd.Wait() function from the built in exec.Cmd, only now I can see that the script makes it all the way to the end, but for some reason is either not returning, or the go program can not tell that is has returned.

Some of what the script is doing is starting or restarting services in /etc/init.d, if one of those launches a process in the backround, since the parent process is the postinstall script could that cause the process to not fully complete in the eye of the go program?

Or any other types of commands that could cause this type of behavior?

I may just have to start removing parts of the script and see if I can get it to complete to narrow down what is causing this to act different then my test script, but its around 100 lines, and the process to get the script into the device is a multi step process.

testscript was something along the lines of

#!/bin/bash

echo "stdOut"
sleep 1
>2& echo "stdErr"
sleep 2
echo "Finsihed"
sleep 1

and this worked as expected(timed out when limit was 1 second, showed completed when timeout was set to 4 seconds)

Should Also note that the process works on other scripts. this is part of an updating process that does a preinstall.sh, then uses this same process to call tar -xvf on the file containing the update, and then goes on to this postinstall script. So most of the time my process succesfully detects that the process has exited, there is just something about the postinstall thats the issue, but I cant think of what would make it hang in go, but exit fine when run from the terminal.

  • 写回答

1条回答 默认 最新

  • douxing5199 2019-05-30 20:11
    关注

    Here's from the docs for Cmd.wait with emphasis:

    func (c *Cmd) Wait() error
    

    Wait waits for the command to exit and waits for any copying to stdin or copying from stdout or stderr to complete.

    This means that it will wait for all processes to close the relevant pipe, not just for the given process to exit. This is a problem when you start processes in the background:

    Here's an example:

    #!/bin/bash
    sleep 3600 &
    echo "Exit"
    

    sleep inherits stdin/out/err and keeps them open for an hour. It will exit immediately in a terminal because Bash doesn't and can't care what has the terminal open:

    $ ./testscript; echo "Returned"
    Exit
    Returned
    $
    

    However, if you pipe to cat, it will wait for all potential data to finish (in case sleep decides to write something later), and bash in turns waits on cat:

    $ ./testscript | cat; echo "Returned"
    Exit
    (Hangs for an hour)
    

    You can fix this by making sure any forked processes won't be writing to the pipe by redirecting somewhere else:

    #!/bin/bash
    sleep 3600 < /dev/null > /dev/null 2>&1 &
    echo "Exit"
    

    Since sleep no longer holds the pipe open, it returns immediately, both in the shell and with Cmd.Wait():

    $ ./testscript | cat; echo "Returned"
    Exit
    Returned
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥30 Matlab打开默认名称带有/的光谱数据
  • ¥50 easyExcel模板 动态单元格合并列
  • ¥15 res.rows如何取值使用
  • ¥15 在odoo17开发环境中,怎么实现库存管理系统,或独立模块设计与AGV小车对接?开发方面应如何设计和开发?请详细解释MES或WMS在与AGV小车对接时需完成的设计和开发
  • ¥15 CSP算法实现EEG特征提取,哪一步错了?
  • ¥15 游戏盾如何溯源服务器真实ip?需要30个字。后面的字是凑数的
  • ¥15 vue3前端取消收藏的不会引用collectId
  • ¥15 delphi7 HMAC_SHA256方式加密
  • ¥15 关于#qt#的问题:我想实现qcustomplot完成坐标轴
  • ¥15 下列c语言代码为何输出了多余的空格