68

在 bash 脚本中终止进程后,如何抑制出现的Terminated消息?

我试过set +bm了,但这不起作用。

我知道另一种解决方案涉及调用exec 2> /dev/null,但这可靠吗?如何重置它以便我可以继续看到 stderr?

4

12 回答 12

149

为了使消息静音,您必须stderr 在消息生成时进行重定向。因为kill命令发送一个信号并且不等待目标进程响应,所以重定向stderr命令kill对你没有好处。内置 bashwait是专门为此目的而制作的。

这是一个非常简单的示例,可以杀死最近的后台命令。(在此处了解有关 $! 的更多信息。

kill $!
wait $! 2>/dev/null

因为两者都kill接受wait多个 pid,所以您也可以进行批量杀戮。这是一个杀死所有后台进程(当然是当前进程/脚本)的示例。

kill $(jobs -rp)
wait $(jobs -rp) 2>/dev/null

我是从bash 被带到这里的:silently kill background function process

于 2011-04-19T21:22:01.720 回答
22

简短的回答是你不能。Bash 总是打印前台作业的状态。监视标志仅适用于后台作业,并且仅适用于交互式 shell,不适用于脚本。

请参阅 jobs.c 中的 notify_of_job_status()。

正如您所说,您可以重定向,因此标准错误指向 /dev/null 但随后您会错过任何其他错误消息。您可以通过在运行脚本的子shell 中进行重定向来使其临时化。这留下了原始环境。

(script 2> /dev/null)

这将丢失所有错误消息,但仅来自该脚本,而不是来自该 shell 中运行的任何其他内容。

您可以通过将新的文件描述符重定向到那里来保存和恢复标准错误:

exec 3>&2          # 3 is now a copy of 2
exec 2> /dev/null  # 2 now points to /dev/null
script             # run script with redirected stderr
exec 2>&3          # restore stderr to saved
exec 3>&-          # close saved version

但我不推荐这个——第一个的唯一好处是它保存了一个子shell调用,同时更复杂,如果脚本改变文件描述符,甚至可能改变脚本的行为。


编辑:

如需更合适的答案,请查看Mark Edgar给出的答案

于 2008-09-17T10:07:39.663 回答
9

解决方案:使用 SIGINT(仅适用于非交互式 shell)

演示:

cat > silent.sh <<"EOF"
sleep 100 &
kill -INT $!
sleep 1
EOF

sh silent.sh

http://thread.gmane.org/gmane.comp.shells.bash.bugs/15798

于 2011-01-31T09:55:05.200 回答
7

也许通过调用将进程与当前的 shell 进程分离disown

于 2008-09-17T09:44:15.303 回答
3

这就是我们都在寻找的吗?

不想要:

$ sleep 3 &
[1] 234
<pressing enter a few times....>
$
$
[1]+  Done                    sleep 3
$

通缉:

$ (set +m; sleep 3 &)
<again, pressing enter several times....>
$
$
$
$
$

如您所见,没有作业结束消息。在 bash 脚本中也适用于我,也适用于终止的后台进程。

'set +m' 禁用当前 shell 的作业控制(参见 'help set')。因此,如果您在子 shell 中输入命令(如在括号中所做的那样),您将不会影响当前 shell 的作业控制设置。唯一的缺点是,如果要检查后台进程是否已终止或评估返回码,则需要将后台进程的 pid 返回到当前 shell。

于 2012-08-30T13:35:40.483 回答
2

这也适用于 killall(对于那些喜欢它的人):

killall -s SIGINT (yourprogram) 

抑制消息...我在后台模式下运行 mpg123。它只能通过发送 ctrl-c (SIGINT) 而不是 SIGTERM (默认) 静默地被杀死。

于 2012-11-04T21:43:10.020 回答
2

由 bash 3.x 和 4.x的Terminated默认信号处理程序记录。只需在子进程的第一个捕获 TERM 信号:

#!/bin/sh

## assume script name is test.sh

foo() {
  trap 'exit 0' TERM ## here is the key
  while true; do sleep 1; done
}

echo before child
ps aux | grep 'test\.s[h]\|slee[p]'

foo &
pid=$!

sleep 1 # wait trap is done

echo before kill
ps aux | grep 'test\.s[h]\|slee[p]'

kill $pid ## no need to redirect stdin/stderr

sleep 1 # wait kill is done

echo after kill
ps aux | grep 'test\.s[h]\|slee[p]'

于 2021-02-24T16:40:07.110 回答
1

disown 为我做了完全正确的事—— exec 3>&2 有很多风险—— set +bm 似乎不能在脚本中工作,只能在命令提示符下工作

于 2011-01-28T21:04:58.983 回答
0

禁用作业通知的另一种方法是将您的命令放置在sh -c 'cmd &'构造中。

#!/bin/bash
# ...
pid="`sh -c 'sleep 30 & echo ${!}' | head -1`"
kill "$pid"
# ...

# or put several cmds in sh -c '...' construct
sh -c '
sleep 30 &
pid="${!}"
sleep 5 
kill "${pid}"
'
于 2013-03-08T17:49:16.907 回答
0

在脚本中添加 ' jobs 2>&1 >/dev/null' 成功,不确定它是否会帮助其他人的脚本,但这里有一个示例。

    while true; do echo $RANDOM; done | while read line
    do
    echo Random is $line the last jobid is $(jobs -lp)
    jobs 2>&1 >/dev/null
    sleep 3
    done
于 2013-05-07T16:34:40.177 回答
-1

简单的:

{ kill $! } 2>/dev/null

优势?可以使用任何信号

前任:

{ kill -9 $PID } 2>/dev/null
于 2013-06-23T05:31:26.183 回答
-1

我发现将 kill 命令放在一个函数中,然后将该函数置于后台会抑制终止输出

function killCmd() {
    kill $1
}

killCmd $somePID &
于 2018-02-10T22:15:15.833 回答