我需要终止一些以 sudo 身份运行的进程,按全名匹配它们,并计算原始命令调用的数量。
对于每个进程有两个进程:命令本身和命令sudo
本身,但我可以解决这个问题:
$ sudo -b perf record sleep 100
$ pgrep -fa '/perf record'
2245700 /usr/lib/linux-tools/.../perf record sleep 100
问题是,当我调用 时sudo pkill
,它会找到、杀死并计数自身:
$ sudo pkill -ef '/perf record' | wc -l
2
在这种情况下,有没有一种简单的方法可以让 pkill 不包含自身?
我尝试过使用 pid 文件,这是可以接受的,但是缺少 pgrep/pkill 文档,并且它似乎无法以基本形式工作:
$ pgrep -f '/perf record' | tee /tmp/pids
$ sudo pkill -F /tmp/pids
killed (pid 2249211)
因为它只会杀死第一个。文档说:
-F, --pidfile file
Read PID's from file. This option is perhaps more useful for pkill than pgrep.
但它的含义含糊不清PID's
。
答案1
尝试:
sudo pkill -ef '/[p]erf record' | wc -l
这是一个使用仅包含单个字母的字符类的技巧p
。
正则表达式正在寻找/perf
,但sudo pkill
命令行有/[p]erf
,但不匹配。
这种方法已经在像这样的命令中使用了几十年ps aux | awk '/[f]oo/ {print $1}'
答案2
pkill
请注意, /中的模式pgrep
是扩展的正则表达式,如 中的模式grep -E
。因此,您可以只提供一个更精确地针对所需进程的正则表达式。在你的情况下,可能是这样的:
sudo pkill -ef '^(/[^/]+)+/perf record'
甚至更好@cas 描述的技巧在他的回答中。
可能值得解释一下为什么需要所有这些:
众所周知,pkill
/pgrep
永远不会匹配自己。然而,他们使用的标准是匹配PID, IE他们检查匹配的 PID 之一是否是他们的他们恰恰排除了这一点。
但是通过 a 运行(使用-f
完整命令行模式选项)sudo
,该pkill
命令最终也会匹配sudo
自己的进程,因为该命令也带有与 a 匹配的命令行字符串裸像你使用的那种模式。显然,sudo
自己的进程与该进程的 PID 不同pkill
,因此pkill
不会将该进程从列表中排除,从而杀死sudo
生成(并且仍然保留)该进程的pkill
进程。
考虑:
$ sudo -b perf record sleep 1234
$ sudo pgrep -fa '/perf record'
1446 /usr/lib/linux-tools/4.2.0-42-generic/perf record sleep 1234
1466 sudo pgrep -fa /perf record # <-- if I issued a pkill, this process, the sudo, would have been killed
在上面的代码片段中,请pgrep
注意自己的进程确实被排除在列表之外。但该sudo
进程还带有一个与裸露的正则表达式匹配的命令行。
通过提供更定制的正则表达式,pkill
不再包含该sudo pkill -ef <...>
进程,因为进程自己的命令行(携带定制的正则表达式)与正则表达式本身不匹配。
关于选项的最后说明-F
。
在我写这篇文章的时候,这个选项仍然存在只读取一个 PID从文件中。所以是的,文档以及程序的--help
输出都具有误导性。来源中的这条评论说的是丑陋的事实。
/* FreeBSD: arg 是一个包含APID匹配*/
答案3
直接获取 pid,避免搜索它:
pid="$(sudo bash -c 'sleep 100 >/dev/null & echo $!')"
sudo kill "$pid"
即使后台进程不输出任何内容,stdout 的重定向也是必要的,因为否则它将被设置为提供 的结果$()
,这将导致$()
它在尝试从子进程读取时挂起,直到后台任务关闭其 stdout 。
这里的主要好处pkill
是您将始终获得正确的 pid,而不是可能杀死与您的搜索匹配的其他进程。例如,如果您同时运行脚本多次,则可能有多个perf
进程匹配[p]erf record
,每个进程都是脚本每次调用的子进程。
答案4
在即将推出的 procps 4.0.1 中,该-A
选项被引入pkill
并pgrep
忽略其祖先。
这意味着您只需运行以下命令即可避免终止sudo
进程:
sudo pkill -Aef '/perf record'