我想终止一个名称较长、以foo
.目前我的计划是使用pkill
(尽管为了测试,为了对过程友好,我一直在检查我的模式pgrep
)。我见过这个问题这表明对于长进程名称,您必须使用-f
来获取整个命令行。这对于获取长进程名称的其余部分非常有用;问题是,然后模式与整个命令行匹配,而不仅仅是进程名称,我不想意外杀死恰好foo
作为参数的不同程序。
所以我相信我想写一个这样的模式:
pgrep -f '^[^\0]*foo'
我所写的\0
意思是零字节——-f
放置在进程的每个参数之前的分隔符。当然\0
不行;该模式实际上匹配任何不是\
或 (ASCII)的字符序列0
。我也尝试过这个:
pgrep -f '^[^'`echo -n '\0'`']*foo'
到这里,我确认我的 shell 为 生成了一个 0 字节echo -n '\0'
。不幸的是,由于传递参数的方式,pgrep
当它看到实际的 0 字节时会停止扫描模式,因此此命令的行为与 完全相同pgrep -f '^[^'
——即,它会抛出有关接收无效模式的错误。
如何编写一个引用其中的零字节(或者,任何不是零字节的字节)的模式?
答案1
pgrep -f
匹配传递给进程执行的最后一个命令的参数的 SPC 字符串联(如/proc/pid/cmdline
Linux 上所示)。它永远不会包含 NUL 字符。
所以你需要这样做:
pgrep -f '^[^ ]*foo( |$)'
/path/with space/foo
尽管您会错过使用as执行某些命令的进程,但它会报告使用asargv[0]
执行命令的进程。foo bar
argv[0]
在任何情况下,都不能将参数中的 NUL 传递给执行的命令,因为命令参数是 NUL 分隔的字符串。
在 中zsh
,您可以将 NUL 传递给内置命令和函数(因为它们不受该限制的影响,execve()
因为它们不是被处决)。所以在 Linux 上,你可以这样做:
print -rC1 /proc/<->(Ne[$'[[ "${$(<$REPLY/cmdline)%%\0*}" = *foo ]]']:t)
查找以argv[0]
结尾的进程foo
。
或者:
print -rC1 /proc/<->/exe(N-.e['[[ $REPLY:A = *foo ]]']:h:t)
对于那些正在执行路径结尾的命令的人foo
(尽管您可能仅限于自己的进程)。
在 Linux 上,进程名称限制为 15 个字节,并且每次调用时都会更改为第一个参数的基本名称的前 15 个字节execve()
,但也可以通过写入/proc/pid/comm
或 with 来更改prctl(PR_SET_NAME)
。进程也可以改变 中的内容/proc/pid/cmdline
。例如参见:
$ perl -lne 'BEGIN{$0 = "foo bar 90123456789"}; print "$ARGV: $_"' /proc/self/{cmdline,comm}
/proc/self/cmdline: foo bar 90123456789
/proc/self/comm: foo bar 9012345