#! / usr / bin / env和进程名称:可移植性的价格?

有很多很好的理由使用#! 在/ usr / bin中/ env的。 底线:它使你的代码更加便携。 好吧,呃。 看一下这个....


我有两个几乎相同的脚本bintest.py

#! /usr/bin/python
import time
time.sleep(5*60)

envtest.py

#! /usr/bin/env python
import time
time.sleep(5*60)

请注意,他们只是在他们的shebangs不同。


bintest.py按预期运行

br@carina:~$ ./bintest.py & ps && killall bintest.py
[1] 15061
  PID TTY          TIME CMD
14625 pts/0    00:00:00 bash
15061 pts/0    00:00:00 bintest.py
15062 pts/0    00:00:00 ps
br@carina:~$ 
[1]+  Terminated              ./bintest.py

但是envtest.py做一些不太理想的事情

br@carina:~$ ./envtest.py & ps && killall envtest.py
[1] 15066
  PID TTY          TIME CMD
14625 pts/0    00:00:00 bash
15066 pts/0    00:00:00 python
15067 pts/0    00:00:00 ps
envtest.py: no process found
br@carina:~$ killall python
br@carina:~$ 
[1]+  Terminated              ./envtest.py

我们看到的是使用#! /usr/bin/env #! /usr/bin/env导致进程接收名称“python”而不是“envtest.py”,从而导致我们的killall无效。 在某种程度上,我们似乎已经为另一种方式交换了一种可移植性:我们现在可以很容易地换出python解释器,但是我们已经在命令行上失去了“kill-ability”。 那是怎么回事? 如果这里有一个最佳实践,那么它是什么?


命令行上的“kill-ability”可以通过使用从shell $!获得的后台进程的PID可移植且可靠地进行处理$! 变量。

$ ./bintest.py & bg_pid=$! ; echo bg_pid=$bg_pid ; ps && kill $bg_pid
[1] 2993
bg_pid=2993
  PID TTY          TIME CMD
 2410 pts/0    00:00:00 bash
 2993 pts/0    00:00:00 bintest.py
 2994 pts/0    00:00:00 ps
$ 
[1]+  Terminated              ./bintest.py
$ 

和envtest.py

$ ./envtest.py & bg_pid=$! ; echo bg_pid=$bg_pid ; ps && kill $bg_pid
[1] 3016
bg_pid=3016
  PID TTY          TIME CMD
 2410 pts/0    00:00:00 bash
 3016 pts/0    00:00:00 python
 3017 pts/0    00:00:00 ps
$ 
[1]+  Terminated              ./envtest.py
$ 

正如@Adam Bryzak所指出的那样,这两种脚本都不会在Mac OS X上设置进程标题。因此,如果该功能是一项严格的要求,那么您可能需要在应用程序中安装和使用python模块setproctitle。

这个Stackoverflow的帖子讨论了在python中设置进程的标题


我不认为你可以依靠使用脚本名称的killall一直工作。 在Mac OS XI上运行这两个脚本后,从ps获得以下输出:

 2108 ttys004    0:00.04 /usr/local/bin/python /Users/adam/bin/bintest.py
 2133 ttys004    0:00.03 python /Users/adam/bin/envtest.py

并运行killall bintest.py结果

No matching processes belonging to you were found

尽管我仍然希望能够通过命令行使脚本语言跨平台且易于监视的解决方案,但如果您只是寻找替代killall <scriptname>来停止自定义服务的解决方案,那么我是如何解决它的:

kill `ps -fC <interpreterName> | sed -n '/<scriptName>/s/^[^0-9]*([0-9]*).*$/1/gp'`

对于那些不太熟悉ps和正则表达式的人来说, ps-f修饰符已经列出了关于进程的一整套“完整”信息,包括它的命令行参数,以及-C指示它将列表过滤为只有命令与下一个命令行参数相匹配。 用pythonnode或其他替换<interpreterName>

sed-n参数告诉它默认情况下不打印任何东西,而正则表达式脚本必须明确指出你想打印什么。

在正则表达式中,第一个/<scriptName>/告诉它将其结果过滤为仅包含内部正则表达式的行。 可以替代<scriptName>envtest ,例如。

s表示替换正则表达式将随之而来。 /^[^0-9]*([0-9]*).*$/是行匹配部分, /1/是替代部分。 在行匹配部分, ^开头的^和最末尾的$意味着匹配必须从行的开始处开始并结束于行的末尾 - 被检查的整个行将被替换。

[^0-9]*涉及几件事情: []用于定义一组允许的字符。 在这部分正则表达式中,短划线-表示一系列字符,因此它扩展为0123456789 。 这里^表示“不”,并且立即表示“匹配不是数字的任何字符”。 之后的星号*表示继续匹配该集合中的字符,直到遇到不匹配的字符(在本例中为数字)。

([0-9]*)有两个部分, ()[0-9]* 。 后者应该易于遵循前面的解释:它只匹配数字,并且尽可能地抓住数字。 ()意思是保存与临时变量匹配的内容。 (在其他RegEx版本中,包括Javascript和Perl,使用() 。)

最后, .*表示匹配每个剩余的字符,如. 意味着任何可能的角色

/1/部说来代替线的与所匹配的部分(其是整个线在这种情况下) 1 ,这是为了保存临时变量的引用(如果出现了2 ()的部分, RegEx中的第一个将是1和第二个2 )。

g之后的意思是“贪婪”,并在遇到的每一行上运行这个匹配的代码,而p意味着打印已达到该点的任何行。

从技术上讲,如果你的脚本有多个副本在运行,那么这将会炸毁,而你真的希望稍微重一点:

ps -fC <interpreterName> | sed -n '/<scriptName>/s/^[^0-9]*([0-9]*).$/kill 1/gp' | bash

如果你想真正复制kill * all *功能,但是这会为你想要杀死的每个脚本生成一个单独的bash shell。

链接地址: http://www.djcxy.com/p/8103.html

上一篇: #! /usr/bin/env and process names: portability at a price?

下一篇: Consuming a rabbitmq message queue with multiple threads (Python Kombu)