使用bash和正则表达式在一行中查找并终止一个进程

794

我经常需要在编程过程中结束一个进程。

现在我所采用的方式是:

[~]$ ps aux | grep 'python csp_build.py'
user    5124  1.0  0.3 214588 13852 pts/4    Sl+  11:19   0:00 python csp_build.py
user    5373  0.0  0.0   8096   960 pts/6    S+   11:20   0:00 grep python csp_build.py
[~]$ kill 5124

我该如何自动提取进程ID并在同一行中杀死它?

像这样:

[~]$ ps aux | grep 'python csp_build.py' | kill <regex that returns the pid>

3
相信我! :'D 你选择的第一个答案比你在答案中提到的解决方法复杂得多。我宁愿选择你的方式。 - Santosh Kumar
检查进程是否存在的最佳方法:https://dev59.com/c3A75IYBdhLWcg3w6Ndj - Trevor Boyd Smith
31个回答

1705

bash中,仅使用您问题中列出的基本工具(1),您应该能够做到:

kill $(ps aux | grep '[p]ython csp_build.py' | awk '{print $2}')

以下是其工作原理的详细信息:

  • ps 命令可以列出所有进程的列表。
  • grep 命令可以基于搜索字符串对其进行筛选,[p] 是一个技巧,用于防止选择实际的 grep 进程本身。
  • awk 命令只会给出每行的第二个字段,即 PID。
  • $(x) 结构表示要执行 x 然后取其输出并将其放在命令行上。上面的管道内的 ps 的输出结果是进程 ID 列表,因此最终得到类似于 kill 1234 1122 7654 的命令。

下面是其实际操作的示例:

pax> sleep 3600 &
[1] 2225
pax> sleep 3600 &
[2] 2226
pax> sleep 3600 &
[3] 2227
pax> sleep 3600 &
[4] 2228
pax> sleep 3600 &
[5] 2229
pax> kill $(ps aux | grep '[s]leep' | awk '{print $2}')
[5]+  Terminated              sleep 3600
[1]   Terminated              sleep 3600
[2]   Terminated              sleep 3600
[3]-  Terminated              sleep 3600
[4]+  Terminated              sleep 3600
并且您可以看到它终止了所有的睡眠进程。
更详细地解释一下grep '[p]ython csp_build.py'的含义:当您执行sleep 3600&后跟ps -ef | grep sleep,您往往会得到两个带有sleep的进程,即sleep 3600grep sleep(因为它们都包含sleep这个单词,这不是什么难事)。

然而,ps -ef | grep '[s]leep'将不会创建一个带有sleepgrep进程,而是创建一个带有命令grep '[s]leep'的进程,这是一个棘手的问题:grep找不到它,因为它正在寻找正则表达式“字符类[s]中的任何字符(基本上只有s)后跟leep”。

换句话说,它正在寻找sleep,但grep进程是grep '[s]leep',它没有该文本sleep

当我被某个SO上的人展示了这种方法之后,我立刻开始使用它,因为

  • 它比添加| grep -v grep少了一个进程;
  • 它既优雅又狡猾,这是一种罕见的组合:-)

(1)如果您限于使用这些基本工具,则有一个巧妙的pgrep命令,它可以根据某些标准查找进程(当然,假设您的系统可用)。

例如,您可以使用pgrep sleep输出所有sleep命令的进程ID(默认情况下,它匹配进程名称)。如果您想像ps中显示的那样匹配整个命令行,则可以执行类似于pgrep -f 'sleep 9999'的操作。

顺便说一下,如果您执行pgrep pgrep,它不会列出自身,因此在这种情况下不需要使用上面显示的棘手过滤器方法。

您可以使用-a来显示完整的进程名称检查您感兴趣的进程是否正确。您还可以使用-u-U将范围限制为自己的进程(或特定的一组用户)。有关更多选项,请参阅pgrep/pkillman页面。

一旦您确信它只会显示您感兴趣的进程,则可以使用相同的参数使用pkill向所有这些进程发送信号。


第一个问题:您的命令只删除第一个进程ID。为了正确删除所有进程,应该在awk后面添加“| xargs”,这样可以将进程ID写入单行。第二个问题:如果使用简单的正则表达式而没有宏字符,此命令会返回错误。在这种情况下,ps命令找到自己的ID,但在运行时kill,ps进程不再存在。 - Xintrea
@Xintrea,你的第一条评论是错误的,这可以从记录中得到证实。kill 命令可以愉快地接受并杀死 多个 PIDs。你的第二个声明是正确的,但无关紧要,因为我从未 建议 使用“不带宏字符的简单正则表达式”(假设那些“宏字符”是 [p] 部分,根据你提供的行为看起来是这样)。如果我建议你做 A,而你决定做 B 并且它不起作用,很难责怪我 :-) 但是,请让我知道如果我误解了你的评论,可能是这种情况。 - paxdiablo

184

如果您拥有pkill命令,

pkill -f csp_build.py

如果你只想对进程名称进行grep匹配(而不是整个参数列表),那么去掉-f选项即可。


121

一句话:

ps aux | grep -i csp_build | awk '{print $2}' | xargs sudo kill -9

  • 打印第二列:awk '{print $2}'
  • sudo 是可选的
  • 运行kill -9 5124kill -9 5373等(kill -15更优雅但稍慢)

额外奖励:

我还在我的.bash_profile中定义了两个快捷功能 (~/.bash_profile用于osx,您必须看看适用于您的*nix机器)。

  1. p 关键字
    • 显示包含关键字的所有P进程
    • 用法例如:p csp_buildp python

bash_profile代码:

# FIND PROCESS
function p(){
        ps aux | grep -i $1 | grep -v grep
}
  1. ka 关键字
    • Kills All 拥有此关键字的进程
    • 示例 e.g: ka csp_build , ka python
    • 可选的 kill level 例如: ka csp_build 15, ka python 9

bash_profile 代码:

# KILL ALL
function ka(){

    cnt=$( p $1 | wc -l)  # total count of processes found
    klevel=${2:-15}       # kill level, defaults to 15 if argument 2 is empty

    echo -e "\nSearching for '$1' -- Found" $cnt "Running Processes .. "
    p $1

    echo -e '\nTerminating' $cnt 'processes .. '

    ps aux  |  grep -i $1 |  grep -v grep   | awk '{print $2}' | xargs sudo kill -klevel
    echo -e "Done!\n"

    echo "Running search again:"
    p "$1"
    echo -e "\n"
}

那个一行的grep命令的问题是它会找到自己(因为它包含csp_build文本)。所以你可以选择以下方式之一:(1)在它消失后(显示错误)尝试杀掉那个grep进程,(2)杀掉一个不同的进程,但它具有相同的PID(在大多数UNIX系统上不太可能,因为它们会循环使用PID,但如果有变化的话可能会发生),或者(3)在grep仍在生成PID时杀掉它,这意味着你不会得到每个进程。请参考我的答案,了解绕过这个问题的巧妙方法。 - undefined
上面的脚本已经处理了这个问题,只需要简单的grep -v命令。 - undefined
是的,我知道这一点,所以我只是简单地提到了这个问题。 - undefined

24
killall -r regexp

-r, --regexp

将进程名称模式解释为扩展正则表达式。


为什么这不是排名第一的?它只用两三个单词就能完全满足要求... - FullStack Alex
如何进行干跑测试?可能的答案是:SIGCONT 表示 "continue",发送它应该相当安全。我用 killall -s SIGCONT -r 'RegexHere' 进行测试。虽然它不会给出进程列表,但确实适用于执行某种形式的“干跑”命令。我猜一个类似的方法就是使用带有 ps aux | grep 'RegexHere' 的正则表达式... - rinogo
1
此外,关于“为什么这不在顶部?”据我所知,这似乎只在实际命令名称上操作,不包括所有命令行参数 - rinogo
@rinogo:哎,SIGCONT绝对不是无害的发送,特别是对于当前处于SIGSTOP控制下的进程 :-) - undefined

20

这将仅返回pid

pgrep -f 'process_name'

要在一行中结束任何进程:

kill -9 $(pgrep -f 'process_name')

或者,如果您知道进程的确切名称,也可以尝试使用pidof:

kill -9 $(pidof 'process_name')

但是,如果您不知道精确的进程名称,pgrep会更好。

如果有多个运行相同名称的进程,并且您想要杀死第一个,则:

kill -9 $(pgrep -f 'process_name' | head -1)

还要注意的是,如果您担心大小写敏感性,那么可以像grep一样添加-i选项。例如:

也就是说:

如果您担心大小写敏感性,则可以添加-i选项,就像在grep中一样。例如:

kill -9 $(pgrep -fi chrome)

有关信号和pgrep的更多信息,请参阅man 7 signalman signalman pgrep


“杀死第一个”方法取决于您知道pgrep生成的顺序,并且同意这就是您想要的那个 :-) - undefined

17

尝试使用

ps aux | grep 'python csp_build.py' | head -1 | cut -d " " -f 2 | xargs kill

不得不稍微修改一下,这样就可以了。谢谢 :) ps aux | grep 'python csp_build.py' | head -1 | cut -d " " -f 5 | xargs kill - Orjanp
3
ps aux | grep 'python csp_build.py' | awk '{print $2}' | xargs kill 对我有用。谢谢。 - Govinnage Rasika Perera
1
记住,孩子们,Awk 可以做到 grep 能做的一切,而且大多数情况下都是简单而优雅的。grep x y | awk '{ z }' 这种微不足道的情况总是最好写成 awk '/x/ { z }' y -- 参见 无用的 grep - tripleee

13

您可以仅使用pkill '^python*'进行正则表达式进程终止。

如果您想在杀死进程之前查看要杀死的进程或查找,请使用pgrep -l '^python*',其中-l还输出进程名称。如果您不想使用pkill,只需使用:

pgrep '^python*' | xargs kill


11

使用pgrep - 在许多平台上都可用:

kill -9 `pgrep -f cps_build`

使用pgrep -f命令将返回所有PID,其中包含"cps_build"关键字


2
如果你有 pgrep,那么你也会有 pkill。一如既往,请不要使用 kill -9,除非你知道为什么kill -15(默认)或 kill -2 不能工作。 - tripleee
这看起来是一个更差的改写@nathanael's answer,省略了错误的-9并使用了适当的现代命令替换语法。请支持那个回答;当然,pkill回答仍然更好。 - tripleee
@tripleee 在这种情况下,kill -9 正是我想要的——以极端偏见终止所有罪犯。此外,我已经使用 kill -9 多年了,没有任何问题。在我看来,总会有一群纯粹主义者和一群实事求是的人之间的阵营,而我属于后者(在这个问题上)。 - a20
你有没有错过“除非你知道为什么”的部分?我完全支持完成任务,但在理解“-9”实际含义之前,这是自毁前程的常见方式之一。 - tripleee
@tripleee 嘿,tripleee,我最近发现你是对的,kill -15 是更好的选择,因为它给应用程序一个优雅地自我关闭的机会。我已经相应地更改了我的代码:https://dev59.com/vXA75IYBdhLWcg3wBkS1#30486159 - a20
这个救了我的命。 - DJ_Stuffy_K

8
解决方案是使用精确模式过滤进程,解析pid,并构建一个参数列表以执行kill进程操作。
ps -ef  | grep -e <serviceNameA> -e <serviceNameB> -e <serviceNameC> |
awk '{print $2}' | xargs sudo kill -9

说明来自文档:

ps实用程序显示标题行,然后是包含有关所有具有控制终端的进程的信息的行。

-e 显示其他用户的进程信息,包括那些

-f 显示uid、pid、父pid、最近CPU使用情况、进程启动时间等信息。

grep实用程序搜索任何给定的输入文件,选择包含指定字符串的行。

-e 模式,--regexp=pattern 在搜索输入时指定一个模式:如果输入行匹配指定的任何模式,则选中该行。 当需要指定多个模式或者模式以短横线(-)开头时,此选项最为有用。

xargs - 构造参数列表并执行命令

kill - 终止或向进程发送信号。

数字 9 信号 - KILL(不可捕获、不可忽略的终止信号)

示例:

ps -ef  | grep -e node -e loggerUploadService.sh -e applicationService.js |
awk '{print $2}' | xargs sudo kill -9

1
这是一个更好(不那么复杂)的答案和很棒的描述/解释... - Zak

7

你可以使用awk和反引号来完成它

ps auxf |grep 'python csp_build.py'|`awk '{ print "kill " $2 }'`

$2在awk中打印第二列,反引号运行打印的语句。

但更简洁的解决方案是让python进程将其进程ID存储在/var/run中,然后您可以简单地读取该文件并杀死它。


你确定不会同时终止进程5124和5373吗?我想这应该不是问题。 - Orjanp
这应该不是问题,但你可以在grep和awk之间添加一个“grep -v grep”来排除grep进程。 - Alexander Kjäll
经过稍微修改的命令测试,但它并没有杀死进程,只是打印了kill <pid>。ps auxf | grep '[p]ython csp_build.py' | awk '{print "kill " $2}' - Orjanp
只需要将打印“kill”$2语句与系统(“kill”$2)交换即可。然后它就可以工作了。 :) - Orjanp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接