Linux bash:静默地杀死后台函数进程
贝壳大师 我有一个bashshell脚本,在这个脚本中我启动了一个后台函数,比如Linux bash:静默地杀死后台函数进程,linux,bash,shell,unix,scripting,Linux,Bash,Shell,Unix,Scripting,贝壳大师 我有一个bashshell脚本,在这个脚本中我启动了一个后台函数,比如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
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
这完全破坏了我的进度条显示的惊人性,否则它会非常酷
如何删除此消息?尝试用以下行替换您的行
kill$foo\u 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
顺便说一句,我不知道你的进度条有多酷,但你看过PipeViewer(pv)吗 在函数开头添加:
trap 'exit 0' TERM
这是我针对类似问题提出的解决方案(希望在长时间运行的进程中显示时间戳)。这实现了一个killsub函数,允许您在知道pid的情况下安静地终止任何子shell。注意,陷阱指令必须包括:如果脚本被中断,子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
正在打印死亡消息,因为进程仍在监视的“作业”列表中。disown命令将从该列表中删除最近生成的进程,这样即使使用SIGKILL(-9),当它被终止时也不会生成调试消息。您可以在之前使用
set+m
来抑制它。有关该错误的更多信息,错误消息应该来自默认的信号处理程序,该处理程序将信号源转储到脚本中。我只在Bash3.x和4.x上遇到了类似的错误。为了始终在任何地方安静地杀死子进程(在bash 3/4/5、dash、ash、zsh上测试),我们可以在子进程的第一个阶段捕获术语信号:
#/垃圾箱/垃圾箱
##假设脚本名为test.sh
foo(){
陷阱“退出0”项##这里是关键
虽然是真的,但一定要睡觉
}
在孩子面前回音
ps辅助“grep”测试\.s[h]\\\124; slee[p]'
福&
foo_pid=$!
睡眠1#等待陷阱完成
杀前回音
ps辅助“grep”测试\.s[h]\\\124; slee[p]'
杀死$foo_pid
睡眠1#等待杀戮结束
杀戮后的回声
ps辅助“grep”测试\.s[h]\\\124; slee[p]'
+1在re:a bash脚本中使用“massively cool:”)即使将kill foo_pid更改为kill$foo_pid,我也无法复制该脚本。@Tanktalus,我认为这是因为在将输出发送到stderr之前,脚本可能已死亡。我在伪代码的末尾添加了一个sleep,这应该可以让您重新创建问题。while[1];do
可以写成而:;do
。这应该与哪个更为集中,但缺少这里的一些答案合并。这是可行的,但不需要在kill$foo_pid
之后添加/dev/null 2>&1
部分作为stderr(这是不需要的文本的来源)在尝试终止不存在的进程以防止kill foo\u pid失败时,已定向到/dev/nullhelp me:没有这样的进程
message@Koen. 是的,但这与背景工作无关。您可以使用2>/dev/null
使kill
本身发出的任何错误消息静音,就像任何命令一样。此答案对后台作业无效的原因是,Bash本身在kill
命令完成后异步输出一条关于已终止作业的状态消息,您无法直接抑制该消息,除非您使用wait
,如接受的答案所示。这对我不起作用。我仍然会收到“15号信号杀死”的信。我正试图通过ssh实现这一点——我启动一个会话并启动一个进程,然后再次使用ssh并终止该进程。当我尝试“wait$pid”时,它表示进程不是子进程(我认为这是一个不同的会话),然后“被信号15杀死”仍然写入终端。在这种情况下有没有办法抑制这种情况?很棒的东西;我建议{kill$foo_-pid&&wait$foo_-pid;}2>/dev/null
,这样也可以在目标进程不再活动的情况下保持沉默。在bash
完成kill
命令之前,是否存在孩子死亡的(远程)可能性,这样就可以生成终止报告了吗?谢谢@mklement0-您还可以使用作业ID的kill&&wait
模式:{kill%1&&wait%1;}2>/dev/null
非常有效。我同意——这是Bash的最佳解决方案。然而,disown是Bash内置的命令,在大多数其他shell中不可用。@mattst:确实值得指出的是,disown
不符合POSIX;但是,它也可以在ksh
和zsh
中找到。而且,使用disown
似乎不仅仅意味着解除当前shell与后台进程的关联:@mklement0感谢您提供的信息。还有有趣的链接nohup
实际上对于我目前使用disown
的几个脚本来说是一个很好的解决方案。因为原始问题disown
肯定是要使用的问题,我相信您已经意识到了。@mattst:disown
对于手头的问题来说很好(如果终端过早死亡,后台作业将在下次尝试写入stdout时死亡),但是,鉴于问题的一般标题,