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
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 vue3页面el-table页面数据过多
  • ¥100 vue3中融入gRPC-web
  • ¥15 kali环境运行volatility分析android内存文件,缺profile
  • ¥15 写uniapp时遇到的问题
  • ¥15 vs 2008 安装遇到问题
  • ¥15 matlab有限元法求解梁带有若干弹簧质量系统的固有频率
  • ¥15 找一个网络防御专家,外包的
  • ¥100 能不能让两张不同的图片md5值一样,(有尝)
  • ¥15 informer代码训练自己的数据集,改参数怎么改
  • ¥15 请看一下,学校实验要求,我需要具体代码