在后台执行进程,不打印“完成”,并获取PID

时间:2014-07-19 18:25:52

标签: bash

这似乎是一件非常微不足道的事情,但我很困惑。

要在后台执行某些操作,请使用&

>>> sleep 5 &
[1] 21763
>>> #hit enter
[1]+  Done                    sleep 5

但是使用bashrc源代码后台脚本输出作业信息非常令人沮丧,因此您可以this进行修复:

>>> (sleep 5 &)

好的,现在我想获得sleepwait的{​​{1}}的PID。不幸的是,它在子shell中运行,因此典型的kill方法不起作用:

$!

所以我想,也许我可以通过这种方式让子shell打印它的PID:

>>> echo $!
21763
>>> (sleep 5 &)
>>> echo $!
21763 #hasn't changed

但是现在当我在子shell中抛出它时,无论我如何尝试捕获子shell的stdout,它似乎都会阻塞,直到>>> sleep 5 & echo $! [1] 21803 #annoying job-start message (stderr) 21803 #from the echo 完成。

sleep

如何在后台运行某些内容,获取其PID并停止打印作业信息" >>> pid=$(sleep 5 & echo $!) "?

5 个答案:

答案 0 :(得分:5)

解决方案A

召唤进程时,将shell的stderr重定向到>/dev/null以获取该召唤实例。我们可以通过复制fd 2来做到这一点,这样我们仍然可以使用重复的fd进行处理。我们在一个块中完成所有这些操作以使重定向成为临时的:

{ sleep 5 2>&3 & pid=$!; } 3>&2 2>/dev/null

现在要防止"完成"稍后显示的消息,我们从作业表中排除了该过程,这是通过disown命令完成的:

{ sleep 5 2>&3 & disown; pid=$!; } 3>&2 2>/dev/null

如果未启用作业控制,则不需要。可以使用set +mshopt -u -o monitor停用作业控制。

解决方案B

我们也可以使用命令替换来召唤进程。我们遇到的唯一问题是进程仍然将自己挂钩到由$()创建的读取stdout的管道,但我们可以通过在它之前复制原始stdout然后使用该文件描述符来解决这个问题:

{ pid=$( sleep 200s >&3 & echo $! ); } 3>&1

如果我们重定向流程可能没有必要。输出像/dev/null

pid=$( sleep 200s >/dev/null & echo $! )

与流程替换类似:

{ read pid < <(sleep 200s >&3 & echo $!); } 3>&1

有些人可能会说process substitution不需要重定向,但问题是可能访问其stdout的进程会很快死亡。例如:

$ function x { for A in {1..100}; do echo "$A"; sleep 1s; done }
$ read pid < <(x & echo $!)
$ kill -s 0 "$pid" &>/dev/null && echo "Process active." || echo "Process died."
Process died.
$ read pid < <(x > /dev/null & echo $!)
$ kill -s 0 "$pid" &>/dev/null && echo "Process active." || echo "Process died."
Process active.
  • 您可以选择使用exec 3>&1创建一个永久性重复的fd,这样您就可以在下一行中使用pid=$( sleep 200s >&3 & echo $! )

答案 1 :(得分:4)

您可以使用read公告来捕获输出:

read -r pid < <(sleep 10 & echo $!)

然后:

ps -p $pid
  PID TTY           TIME CMD
78541 ttys001    0:00.00 sleep 10

答案 2 :(得分:2)

试试这个:

pid=$((sleep 5 & echo $!) | sed 1q)

答案 3 :(得分:2)

bash中的set +m禁用监控模式。换句话说,它消除了令人讨厌的Done消息。 要再次启用,请使用set -m

例如:

$ set +m
$ (sleep 5; echo some) &
[1] 23545 #still prints the job number

        #after 5 secs
some
$  #no Done message...

答案 4 :(得分:0)

我找到了一个好方法,不需要子shell,将保持父子关系。 自:[1] 21763[1]+ Done sleep 5都是stderr,即&2。 我们可以将&2重定向到/dev/null,这里是代码:

exec 7>&2 2>/dev/null # Here backup 2 to 7, and redirect 2 to /dev/null
sleep 5
wait
exec 2>&7 7>&- # here restore 7 to 2, and delete 7.

请参阅:Using exec

相关问题