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 MATLAB动图问题
  • ¥15 有段代码不知道怎么理解,const isToken = (config.headers || {}).isToken === false
  • ¥100 采用栈和深度优先算法对我的代码进行修改显示路径
  • ¥15 pointnet2包安装
  • ¥20 射频功率问题,解答者有酬谢!
  • ¥80 构建降雨和积水的预测模型
  • ¥15 #Qt Transform setTransform()在鼠标拖动移动视角是一致在原地不动,无法变换视角(细微观察似乎视图有在原地抖动),无法变换视角(细微观察似乎视图有在原地抖动)
  • ¥50 如何利用无人机拍摄的数码照片测量鸟卵的长短径
  • ¥100 github贡献者给与奖励
  • ¥15 使用DS18B20+ESP8266获取温度数据返回-127.00