6

有很多很好的 理由使用 #! /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 时间命令
14625 点/0 00:00:00 重击
15061 分/0 00:00:00 bintest.py
15062 点/0 00:00:00 ps
br@carina:~$
[1]+ 终止 ./bintest.py

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

br@carina:~$ ./envtest.py & ps && killall envtest.py
[1] 15066
  PID TTY 时间命令
14625 点/0 00:00:00 重击
15066 分/0 00:00:00 蟒蛇
15067 点/0 00:00:00 ps
envtest.py:找不到进程
br@carina:~$ killall python
br@carina:~$
[1]+ 终止 ./envtest.py

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

4

4 回答 4

2

命令行上的“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 中设置进程标题

于 2012-04-22T02:09:21.833 回答
0

我认为您不能一直依赖killall使用脚本名称来工作。ps在 Mac OS XI上运行两个脚本后得到以下输出:

 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
于 2011-06-30T03:45:57.813 回答
0

虽然我仍然想要一个使脚本语言既跨平台又易于从命令行监控的解决方案,但如果您只是在寻找killall <scriptname>停止自定义服务的替代方法,以下是我解决它的方法:

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

对于那些不太熟悉 ps 和正则表达式的人,ps-f修饰符让它列出有关进程的“完整”信息集,包括它的命令行参数,并-C告诉它过滤列表以仅匹配下一个命令的命令-line 参数。替换<interpreterName>pythonnode或其他。

sed-n参数告诉它默认情况下不打印任何内容,并且正则表达式脚本必须明确指示您要打印某些内容。

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

s表示将遵循替换正则表达式。/^[^0-9]*\([0-9]*\).*$/是行匹配部分并且/\1/是替换部分。在行匹配部分,^开头和$结尾的 表示匹配必须从行首开始并在行尾结束——被检查的整行将被替换。

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

\([0-9]*\)两个部分,\(\)[0-9]*。后者应该很容易从前面的解释中理解:它只匹配数字,并尽可能多地抓取。将\(\)匹配的内容保存到临时变量的意思。(在其他 RegEx 版本中,包括 Javascript 和 Perl,()被使用。)

最后,.*匹配每个剩余字符的方法,.即任何可能的字符。

/\1/部分表示将行的匹配部分(在这种情况下为整行)替换为\1,这是对保存的临时变量的引用(如果有两个\(\)部分,则 RegEx 中的第一个将\1是第二\2)。

g后来的意思是“贪婪”并在遇到的每一行上运行这个匹配的代码,以及p打印到达这一点的任何行的意思。

从技术上讲,如果您运行脚本的多个副本,这将会爆炸,并且您真的想要稍微重一点:

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

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

于 2012-04-17T20:18:20.973 回答
0

在评论中,您说问题在于不同的系统(尤其是 MacOS 和 Linux)将可执行文件放在不同的目录中。

您可以通过在两个系统上创建具有相同完整路径的目录并创建指向可执行文件的符号链接来解决此问题。

在 Ubuntu、Solaris 和 Cygwin 上进行的实验表明,以 shebang 命名的可执行文件可以是符号链接。(我无法访问 MacOS 系统,所以我不确定它是否可以在那里工作。)

例如,在我的 Ubuntu 系统上:

$ cat hello.bash
#!/tmp/bin/bash

echo Yes, it works
$ ./hello.bash
-bash: ./hello.bash: /tmp/bin/bash: bad interpreter: Permission denied
$ mkdir /tmp/bin
$ ln -s /bin/bash /tmp/bin/.
$ ./hello.bash
Yes, it works
$ 

在所有相关系统上设置公​​共目录无疑是不方便的。(我用于/tmp此示例;不同​​的位置可能会更好。)

我不确定这将如何与 交互killall,但值得尝试。

于 2012-04-17T20:37:28.580 回答