我需要杀死一些作为 sudo 运行的进程,按全名匹配它们,并计算原始命令调用的次数。
对于每个进程,有两个进程:命令本身和sudo
一个,但我可以解决这个问题:
$ sudo -b perf record sleep 100
$ pgrep -fa '/perf record'
2245700 /usr/lib/linux-tools/.../perf record sleep 100
Run Code Online (Sandbox Code Playgroud)
问题是,当我调用时sudo pkill
,它会找到、杀死并计算自己:
$ sudo pkill -ef '/perf record' | wc -l
2
Run Code Online (Sandbox Code Playgroud)
在这种情况下,是否有一种简单的方法让 pkill 不包含自身?
我试过使用 pid 文件,这可能是可以接受的,但是缺少 pgrep/pkill 文档,而且它似乎不能以基本形式工作:
$ pgrep -f '/perf record' | tee /tmp/pids
$ sudo pkill -F /tmp/pids
killed (pid 2249211)
Run Code Online (Sandbox Code Playgroud)
因为它只会杀死第一个。文档说:
-F, --pidfile file
Read PID's from file. This option is perhaps more useful for pkill than pgrep.
Run Code Online (Sandbox Code Playgroud)
但它的含义是模棱两可的PID's
。
cas*_*cas 13
尝试:
sudo pkill -ef '/[p]erf record' | wc -l
Run Code Online (Sandbox Code Playgroud)
这是一个使用仅包含单个字母的字符类的技巧p
。
正则表达式正在寻找/perf
,但sudo pkill
命令行有/[p]erf
,这不匹配。
这种方法已经在像这样的命令中使用了几十年 ps aux | awk '/[f]oo/ {print $1}'
请注意,pkill
/中的模式pgrep
是扩展的正则表达式,如grep -E
. 因此,您可以只提供一个更精确地针对所需进程的正则表达式。在你的情况下,可能是这样的:
sudo pkill -ef '^(/[^/]+)+/perf record'
Run Code Online (Sandbox Code Playgroud)
可能值得解释为什么需要所有这些:
众所周知,pkill
/pgrep
从不匹配自己。然而,他们使用的标准是通过匹配PID,即他们检查匹配的 PID之一是否是他们的,并且他们精确地排除了那个。
但是通过 a 运行(使用-f
完整命令行模式选项)sudo
,该pkill
命令最终sudo
也会匹配自己的进程,因为那个进程也带有一个命令行字符串,该字符串与您使用的那种裸模式匹配。显然sudo
自己的进程与那个进程不同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
Run Code Online (Sandbox Code Playgroud)
在上面的代码片段音符是怎么pgrep
的自己的过程列表确实排除。但是该sudo
过程还带有一个与裸正则表达式匹配的命令行。
通过提供更定制的正则表达式,pkill
不再包含sudo pkill -ef <...>
进程,因为进程自己的命令行携带定制的正则表达式,与正则表达式本身不匹配。
关于-F
选项的最后说明。
在我写这篇文章的时候,那个选项仍然只从文件中读取一个 PID。所以是的,文档以及程序的--help
输出具有误导性。消息来源中的这条评论说的是丑陋的事实。
/* FreeBSD: arg 是一个包含要匹配的PID的文件*/