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

时间:2019-05-30 05:23:53

标签: bash go

我有一个bash脚本postinstall.sh,在从终端运行时可以正常执行

./postinstall.sh

脚本运行一堆命令,创建目录,chown,chmod文件和目录,

脚本结尾处是

echo "Done"
exit 0

当我以exec.Cmd的身份运行它并调用cmd.Wait()函数时,它永远不会返回。

因此,我修改了代码以使用https://github.com/go-cmd/cmd中的软件包,因为我希望能够记录“实时”运行的脚本的输出,还可以检测脚本的完成时间。因此,经过一番工作,我现在可以将Cmd的stdOut记录到日志级别STATUS上的日志记录中,而Cmd的stdErr记录到我的logLevel错误中。我还可以为命令设置超时,并实时记录其进度。

在一些简单的情况下,此新过程可以按预期工作,但是当我在其中放下大型的postinstall.sh脚本时,它将一直运行到最后,然后挂起,最终超时。在命令行上手动运行时,此过程需要几秒钟,我将超时设置为30秒。日志显示

STATUS- Done

这是

之前的行
exit 0

go-cmd软件包的文档显示了一种非锁定方式来告诉进程已完成,我正在检查(并测试了我的测试用例)我也在检查cmd.Status()。finished标志(布尔),并且都没有报告该过程已完成。这与我仅使用内置exec.Cmd中的cmd.Wait()函数所看到的结果相同,只是现在我可以看到脚本一直执行到最后,但由于某种原因,它要么没有返回,否则go程序无法判断已返回。

该脚本正在执行的某些操作是在/etc/init.d中启动或重新启动服务,如果其中一个在后台启动了一个进程,因为父进程是postinstall脚本,可能会导致该进程无法完全运行完成go程序中的程序?

或者其他任何类型的命令都可能导致这种行为?

我可能只需要开始删除脚本的某些部分,看看是否可以完成它,以缩小导致此脚本与我的测试脚本有所不同的原因,但是大约100行,以及获取脚本的过程将脚本导入设备是一个多步骤过程。

testscript类似于

#!/bin/bash

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

并且按预期工作(限制为1秒时超时,超时设置为4秒时显示完成)

还应注意,该过程适用于其他脚本。 这是执行preinstall.sh的更新过程的一部分,然后使用相同的过程对包含更新的文件调用tar -xvf,然后继续执行此后安装脚本。因此,在大多数情况下,我的进程成功检测到该进程已退出,后安装问题才是问题所在,但我想不出什么原因会使它挂起,但是从终端运行时可以退出。

1 个答案:

答案 0 :(得分:0)

这里是Cmd.wait的文档中的重点:

func (c *Cmd) Wait() error
     

等待命令退出,并等待任何复制到stdin或从stdout或stderr复制完成。

这意味着它将等待所有进程关闭相关管道,而不仅仅是等待给定进程退出。当您在后台启动进程时,这是一个问题:

这是一个例子:

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

sleep继承stdin / out / err并使它们保持打开状态一个小时。它会在终端机中立即退出,因为Bash并不在意终端机打开了什么:

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

但是,如果通过管道传输到cat,它将等待所有潜在数据完成(以防sleep决定以后再写东西),bash依次等待cat

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

您可以通过重定向其他地方来确保任何分叉的进程都不会写入管道来解决此问题:

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

由于sleep不再使管道保持打开状态,因此它立即在外壳中以及Cmd.Wait()中返回:

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