如何运行有时间限制的后台命令并读取其输出(没有超时命令)

时间:2017-11-05 11:38:01

标签: bash cross-platform

我在看https://stackoverflow.com/a/10225050/1737158 在同一个Q中,timeout命令有一个答案,但并不是所有操作系统,所以我想避免它。

我尝试做的是:

demo="$(top)" &
TASK_PID=$!
sleep 3
echo "TASK_PID: $TASK_PID"
echo "demo: $demo"

我期望在$ demo变量中没有任何内容,而top命令永远不会结束。

现在我得到一个空的结果。哪个是“可以接受的”,但是当我重新使用与应该返回值的命令相同的东西时,我仍然得到一个空结果,这是不行的。 E.g:

demo="$(uptime)" &
TASK_PID=$!
sleep 3
echo "TASK_PID: $TASK_PID"
echo "demo: $demo"

这应该返回正常运行时间结果,但事实并非如此。我也尝试通过TASK_PID杀死进程,但我总是得到。如果命令失败,我希望以某种方式捕获stderr。它可以是不同的变量,但必须被捕获而不会被泄露出来。

1 个答案:

答案 0 :(得分:1)

执行var=$(cmd) &

时会发生什么

让我们首先注意bash中的simple command具有以下形式:

[variable assignments] [command] [redirections]

例如

$ demo=$(echo 313) declare -p demo
declare -x demo="313"

根据manual

  

[..]每个变量赋值中=之后的文本在分配给变量之前经历了波浪扩展,参数扩展,命令替换,算术扩展和引用删除。

此外,在展开上面的[command]之后,第一个单词被视为命令的名称,但是:

  

如果没有命令名称变量赋值会影响当前的shell环境。否则,变量将添加到已执行命令的环境中,不会影响当前的shell环境。

因此,正如预期的那样,当运行demo=$(cmd)时,$(..)命令替换的结果将分配给当前shell中的demo变量 。 / p>

需要注意的另一点是与背景运算符&相关。它在所谓的lists上运行,它是一个或多个pipelines的序列。也:

  

如果控制操作员&终止命令,则shell 在子shell中异步执行命令 。这称为在后台执行命令

最后,当你说:

$ demo=$(top) &
# ^^^^^^^^^^^ simple command, consisting ONLY of variable assignment

这个简单的命令在子shell中执行(称之为s1),其中$(top)在另一个子shell中执行(称之为s2),此命令替换的结果是分配给shell demo内的变量s1。由于没有给出命令,在赋值变量后,s1终止,但父shell永远不会接收在子(s1)中设置的变量。

与后台流程进行沟通

如果您正在寻找以异步方式与流程进行通信的可靠方式,则可以考虑bash中的coprocesses或其他POSIX环境中的named pipes (FIFO)

协同处理设置更简单,因为coproc会为您设置管道,但请注意,如果在写入任何输出之前终止进程,您可能无法可靠地读取它们。

#!/bin/bash
coproc top -b -n3
cat <&${COPROC[0]}

FIFO 设置看起来像这样:

#!/bin/bash

# fifo setup/clean-up
tmp=$(mktemp -td)
mkfifo "$tmp/out"
trap 'rm -rf "$tmp"' EXIT

# bg job, terminates after 3s
top -b >"$tmp/out" -n3 &

# read the output
cat "$tmp/out"

但请注意,如果在阻止模式下打开FIFO,则在有人打开它进行读取(并开始读取)之前,编写器将无法写入。

超时后杀人

您将如何终止后台进程取决于您使用的设置,但对于上面的简单coproc案例:

#!/bin/bash
coproc top -b
sleep 3
kill -INT "$COPROC_PID"
cat <&${COPROC[0]}