为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
有只小跳蛙 2023-06-19 17:15:11
我有一个从终端运行时正常执行的 bash 脚本 postinstall.sh./postinstall.sh该脚本运行一系列命令,创建目录、chown、chmod 文件和目录,在脚本的末尾是echo "Done"exit 0当我在 go 中将其作为 exec.Cmd 运行并调用 cmd.Wait() 函数时,它永远不会返回。所以我修改了我的代码以使用来自https://github.com/go-cmd/cmd的包,因为我希望能够记录我“实时”运行的脚本的输出,但也可以检测它们何时完成. 因此,经过一系列工作后,我现在可以将 Cmd 的 stdOut 记录到我的 logLevel STATUS 日志中,并将 Cmd 的 stdErr 记录到我的 logLevel ERROR。我还可以为命令设置超时,并实时记录其进度。这个新进程在一些简单的情况下按预期工作,但是当我向它扔大 postinstall.sh 脚本时,它一直运行到最后,然后挂起,最终超时。在命令行上手动运行时,该过程需要几秒钟,我将超时设置为 30 秒。日志记录显示STATUS- Done就在exit 0go-cmd 包的文档显示了一种非锁定方式来告诉进程已经完成,我正在检查(并处理我的测试用例)我也在检查 cmd.Status().finished 标志(bool)并且两者都没有其中报告该过程已完成。这与我通过使用内置 exec.Cmd 中的 cmd.Wait() 函数看到的结果相同,直到现在我才能看到脚本一直到最后,但由于某种原因不是正在返回,或者 go 程序无法判断已经返回。该脚本正在做的一些事情是在 /etc/init.d 中启动或重新启动服务,如果其中一个在后台启动一个进程,因为父进程是安装后脚本,这可能会导致该进程在围棋之眼?或可能导致此类行为的任何其他类型的命令?我可能只需要开始删除部分脚本,看看我是否可以完成它以缩小导致它与我的测试脚本不同的原因,但它大约有 100 行,以及将脚本放入设备是一个多步骤的过程。testscript 类似于#!/bin/bashecho "stdOut"sleep 1>2& echo "stdErr"sleep 2echo "Finsihed"sleep 1这按预期工作(限制为 1 秒时超时,超时设置为 4 秒时显示完成)还应注意该过程适用于其他脚本。这是执行 preinstall.sh 的更新过程的一部分,然后使用相同的过程对包含更新的文件调用 tar -xvf,然后继续执行此安装后脚本。所以大多数时候我的进程成功检测到进程已经退出,这就是安装后的问题,但我想不出是什么让它挂起,但从终端运行时退出正常。
查看完整描述

1 回答

?
心有法竹

TA贡献1866条经验 获得超5个赞

这是来自Cmd.wait的文档,重点是:


func (c *Cmd) Wait() error

Wait 等待命令退出并等待任何复制到 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不再保持管道打开,它会立即返回,无论是在 shell 中还是在Cmd.Wait():


$ ./testscript | cat; echo "Returned"

Exit

Returned


查看完整回答
反对 回复 2023-06-19
  • 1 回答
  • 0 关注
  • 146 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信