首页 > 解决方案 > bash 脚本中的 coreutils 超时对应用程序不透明

问题描述

我在 bash 脚本中通过 /usr/bin/timeout 执行应用程序时遇到问题。在这种特定情况下,这是一个简单的 python 结构脚本(结构版本 1.14) 为了安装此版本的结构库运行: pip install "fabric<2" 新结构 2.x 没有复制。

导致问题的 Shell 脚本:

[root@testhost:~ ] $ cat testNOK.sh
#!/bin/bash
timeout 10 ./test.py
echo "RETCODE=$?"
[root@testhost:~ ] $ ./testNOK.sh
[localhost] run: echo Hello!
RETCODE=124
[root@testhost:~ ] $

类似的脚本(没有超时)工作正常

[root@testhost:~ ] $ cat testOK.sh
#!/bin/bash
./test.py
echo "RETCODE=$?"
[root@testhost:~ ] $ ./testOK.sh
[localhost] run: echo Hello!
[localhost] out: Hello!
[localhost] out:

RETCODE=0
[root@testhost:~ ] $

从 bash 命令行手动执行超时工作正常:

[root@testhost:~ ] $ timeout 10 ./test.py && echo "RETCODE=$?"
[localhost] run: echo Hello!
[localhost] out: Hello!
[localhost] out:

RETCODE=0
[root@testhost:~ ] $

Python2.7 test.py脚本

[root@testhost:~ ] $ cat test.py
#!/usr/bin/python
from fabric.api import run, settings

with settings(host_string='localhost', user='root', password='XXXXX'):
    run('echo Hello!')
[root@testhost:~ ] $

我在不同的 Linux 发行版上观察到了相同的行为。

现在的问题是,为什么在 bash 脚本中通过超时执行的应用程序会以不同的方式表现,以及解决这个问题的最佳方法是什么?

标签: pythonlinuxbashterminaltimeout

解决方案


您需要timeout使用以下 --foreground选项调用:

timeout --foreground ./test.py

仅当timeout命令不是从交互式 shell 执行时(即,如果它是从脚本文件执行),才需要这样做。

timeout信息页面引用:

‘--foreground’
     Don’t create a separate background program group, so that the
     managed COMMAND can use the foreground TTY normally.  This is
     needed to support timing out commands not started directly from an
     interactive shell, in two situations.
       1. COMMAND is interactive and needs to read from the terminal for
          example
       2. the user wants to support sending signals directly to COMMAND
          from the terminal (like Ctrl-C for example)

在这种情况下实际发生的是结构(或调用的东西)正在调用tcsetattr以关闭终端回声。我不知道为什么,但我想这与用于(不)收集用户密码的过程有关。(我只是在 strace 中看到它;我没有尝试找到调用。)尝试从后台进程更改 tty 配置将导致进程阻塞,直到它重新获得对 tty 的控制,这就是正在发生的事情。

不使用时不会发生,timeout因为bash不创建后台程序组。我想 fabric 2 避免调用tcsetattr.

您也可以通过避免基于密码的 SSH 身份验证来避免该问题,但我没有尝试过。

您还可以通过重定向stdin/dev/null(在timeout命令中或在 shell 脚本的调用中)来避免该问题。如果您不需要将标准输入转发到远程命令(并且您可能不需要),那也可能是有用。


推荐阅读