【问题标题】:How to run a time-limited background command and read its output (without timeout command)如何运行限时后台命令并读取其输出(无超时命令)
【发布时间】:2018-04-17 16:20:43
【问题描述】:

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

我尝试做的是:

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

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

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

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

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

【问题讨论】:

  • 如果bash可以在系统上编译,那么timeout也可以编译。
  • 这里的问题是整条生产线都是作为后台进程运行的。您运行一个分配变量 demo 的 shell。然后 shell 结束并且 demo 变量丢失了(它之前已经丢失了,因为只对这个分叉的 shell 可用)。输出很容易提示:[1]+ Done demo="$(uptime)"

标签: bash cross-platform


【解决方案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 变量

还有一点需要注意,与后台操作符&有关。它在所谓的lists 上运行,它们是一个或多个pipelines 的序列。另外:

如果命令被控制运算符& 终止,则shell 在子shell 中异步执行命令。这称为在后台执行命令

最后,当你说:

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

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

与后台进程通信

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

Coprocess 设置更简单,因为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]}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-29
    • 2011-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-16
    • 1970-01-01
    相关资源
    最近更新 更多