Bash: 静默杀死后台函数进程

贝壳大师,

我有一个 bash shell 脚本,在这个脚本中,我启动一个后台函数,比如 foo(),来显示一个无聊而冗长的命令的进度条:

foo()
{
while [ 1 ]
do
#massively cool progress bar display code
sleep 1
done
}


foo &
foo_pid=$!


boring_and_long_command
kill $foo_pid >/dev/null 2>&1
sleep 10

现在,当 foo死了,我看到下面的文字:

/home/user/script: line XXX: 30290 Killed                  foo

这完全破坏了我的超级酷的进度条显示。

我怎样才能摆脱这条信息?

43820 次浏览

试着用下面的代码行代替你的代码行 kill $foo_pid >/dev/null 2>&1:

(kill $foo_pid 2>&1) >/dev/null

更新 :

这个答案是不正确的,原因@mklement0在他的评论中解释道:

这个答案对背景工作没有效果的原因是 在 kill 命令完成之后,异步地 Bash 本身, 输出关于已终止作业的状态消息,但不能输出 直接禁止-除非使用等待,如在已接受的答案中。

这种“黑客手段”似乎奏效了:

# Some trickery to hide killed message
exec 3>&2          # 3 is now a copy of 2
exec 2> /dev/null  # 2 now points to /dev/null
kill $foo_pid >/dev/null 2>&1
sleep 1            # sleep to wait for process to die
exec 2>&3          # restore stderr to saved
exec 3>&-          # close saved version

它的灵感来自 给你。世界秩序已经恢复。

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

顺便说一句,我不知道你的进度条有多酷,但是你看过管道浏览器(pv)吗? http://www.ivarch.com/programs/pv.shtml

在函数的开头添加:

trap 'exit 0' TERM

这是我针对类似问题提出的一个解决方案(希望在长时间运行的进程中显示时间戳)。这实现了一个 kill sub 函数,该函数允许您在知道 pid 的情况下悄悄地终止任何 subshell。注意,包含陷阱指令很重要: 如果脚本被中断,子 shell 将不会继续运行。

foo()
{
while [ 1 ]
do
#massively cool progress bar display code
sleep 1
done
}


#Kills the sub process quietly
function killsub()
{


kill -9 ${1} 2>/dev/null
wait ${1} 2>/dev/null


}


foo &
foo_pid=$!


#Add a trap incase of unexpected interruptions
trap 'killsub ${foo_pid}; exit' INT TERM EXIT


boring_and_long_command


#Kill foo after finished
killsub ${foo_pid}


#Reset trap
trap - INT TERM EXIT

另一种方法是:

    func_terminate_service(){


[[ "$(pidof ${1})" ]] && killall ${1}
sleep 2
[[ "$(pidof ${1})" ]] && kill -9 "$(pidof ${1})"


}

叫它

    func_terminate_service "firefox"

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

#!/bin/bash


foo()
{
while [ 1 ]
do
sleep 1
done
}


#foo &
#foo_pid=$!


export -f foo
foo_pid=`sh -c 'foo & echo ${!}' | head -1`


# if shell does not support exporting functions (export -f foo)
#arg1='foo() { while [ 1 ]; do sleep 1; done; }'
#foo_pid=`sh -c 'eval "$1"; foo & echo ${!}' _ "$arg1" | head -1`




sleep 3
echo kill ${foo_pid}
kill ${foo_pid}
sleep 3
exit

我自己也碰到过这种情况,并且意识到“断绝关系”正是我们所寻找的。

foo &
foo_pid=$!
disown


boring_and_long_command
kill $foo_pid
sleep 10

正在打印死亡消息,因为该进程仍然在被监视的“作业”的 shell 列表中。Disown 命令将从此列表中删除最近生成的进程,这样即使使用 SIGKILL (- 9) ,在杀死该进程时也不会生成调试消息。

您可以在此之前使用 set +m来抑制它。关于该 给你的更多信息

错误消息应该来自默认的信号处理程序,该处理程序将信号源转储到脚本中。我只在 bash 3.x 和4.x 上遇到过类似的错误。为了总是在任何地方悄悄地终止子进程(在 bash 3/4/5、 ash、 ash、 zsh 上测试) ,我们可以在子进程的第一个位置捕获 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 &
foo_pid=$!


sleep 1 # wait trap is done


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


kill $foo_pid


sleep 1 # wait kill is done


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