处理流程

很多读者对周二的反馈意见“如何使用AppleScript和Perl确定某个应用程序是否正在运行”以下是重点。

捆绑标识符

斯文-SPORST通过电子邮件指出,您可以通过名称,显示的名称和创建者类型查询系统事件以查找进程,但有点好奇 - 但是通过包标识符即你可以这样做:

tell application "System Events"
  count (every process whose creator type is "R*ch")
end tell

但你不能做这样的事情:

tell application "System Events"
  count (every process whose bundle identifier is "com.barebones.bbedit")
end tell

考虑到系统事件是Mac OS X的新功能,这似乎是一个奇怪的疏忽,而捆绑标识符是唯一标识应用程序的“Mac OS X”方式它们也更容易记住,通常很容易猜到。

聪明的Endianness解决方法苹果::进程

我周二提到有用的苹果::进程Perl模块作为Mac OS X的Perl安装的一部分提供,在与基于Intel的Mac一起提供的版本中有一些与字节顺序相关的错误也就是说,创建者类型代码是相反的。

在撰写周二的文章时,我考虑建议一种解决方法,包括检查创建者类型是向前还是向后,如果你在任何一个方向都匹配,就认为它是一个命中那是容易工作得很好,但太过狡猾的黑客支持。

读者Patrick McCafferty通过电子邮件发送了一个更好的解决方法:按名称寻找一个流程知道必须运行,比如说,登录窗口,并检查其创建者类型(登录窗口的“lgnw”)如果它颠倒过来,那么你就知道你在基于英特尔的Mac上运行了旧的(错误的)版本苹果::进程,您可以通过手动反转所有创建者类型来解决错误。

It’s easier, of course, to just match by process names (which aren’t affected by the endianness bug) instead of creator codes, but McCafferty’s suggestion was just too clever not to pass along.

P.S- 关于什么PS

然而,最常见的问题 - 到目前为止 - 是“为什么不直接使用PSgrep的“我在星期二确实提到了这种技术,然后再介绍如何在Perl中执行嵌入式AppleScript,写道:

我想大多数Unixheads会做的是管道结果ps -aux通过grep的,但从Perl调用AppleScript很容易[...]

我没有更详细地介绍它的原因是,要使它正确起来可能有点棘手例如,几乎所有通过电子邮件发送建议的人都说错了一些东西

ps -x | grep -c Safari

简要解释上述命令看起来完全胡言乱语的读者的好处:PS命令行工具为您提供了一个正在运行的进程列表,每行一个如果你只是输入PS如果没有选项,您只会获得属于当前用户(即您)的进程列表,这些进程具有“控制终端”,这或多或少意味着它们是在命令行启动的进程添加-X开关告诉了PS命令显示所有进程,包括那些“普通”Mac OS X应用程序,这就是我们想要的。

grep的命令逐个遍历文本行,将正则表达式应用于每一行,查找匹配项在上面的例子中,模式是文字字符串“Safari”该-C选项告诉grep的only to count the number of matching lines; without specifying that option,grep的将完整打印匹配的行管道(|)字符将两个命令连接在一起所以,首先,ps -xruns and generates a list of running processes, one per line; those lines are用管道输送 grep -c Safari,计算这些行中有多少匹配模式“Safari”If Safari isn’t running, the result will be “0”; if it is, the result should be “1”.

在大多数情况下,包括上面的示例,我们正在寻找Safari是否正在运行,这将起作用但在其他情况下,它不会起作用 - 至少不会如预期的那样例如,如果您尝试相同的操作来查看iTunes是否正在运行:

ps -x | grep -c iTunes

对于大多数人来说,该命令的结果是总是至少是“1”,即使iTunes没有运行问题是有一个名为“iTunesHelper”的隐形后台应用程序,1并且grep模式“iTunes”也匹配该名称,除非您已禁用它,否则iTunesHelper始终在运行如果我们想要准确匹配流程名称,我们需要更精确的grep模式。

正则表达式的问题在于,编写一个表达式通常很容易平时工作,但很难写出一个总是作品你应该尝试匹配你所需要的那一部分,而不仅仅是尝试匹配你正在寻找的那一行整个线,包括您正在寻找的部分和您不感兴趣的部分这样“AppName Helper”就不会被误认为是“AppName”。

这是一行输出ps -x好像:

696  ??  S    0:01.22 /Applications/TextEdit.app/Contents/MacOS/TextEdit

这是进程ID(每个进程的唯一编号;同一程序的多个实例获得自己的ID),我们不关心的几列,然后是运行程序的完整路径So imagine you were trying to test if a hypothetical app named “Content” were running; if you tried this:

ps -x | grep -c Content

你的应用程序包中有一个“Contents”文件夹,每个应用程序都匹配 - 这几乎意味着你使用的每一个应用程序(当我刚刚运行该命令进行测试时,结果为“52”。)

我们可以缩小范围的一种方法是通过PSthe-Coption on the command line; this option tellsPS只有列表中的进程名称,而不是它的完整路径典型的输出线来自ps -xc看起来像这样:2

696  ??  S    0:01.22 TextEdit

All we’re interested in is the name of process; for our purposes here, those other columns aren’t interesting因此,寻找TextEdit的一种匹配整行的方法是:

ps -xc | egrep -c "^[ ]*[0-9]+[ ]+[^ ]+[ ]+[^ ]+[ ]+[^ ]+[ ]+TextEdit$"

上帝知道我爱我正则表达式,3但是避免制作如上所述的模式是导致我最初跳过这种技术的原因。

但最终我们可以做到这一点很多更轻松该模式的丑陋部分是与输出列相匹配的位PSthat we aren’t interested in如果我们可以说,我们可以避免这项工作PS我们对流程名称不感兴趣 - 谢谢PS到目前为止 - 未知 - 对我来说-o选项,这正是我们的意思能够做(感谢读者Sam Vaughn提供此解决方案。)

一些典型的输出线来自ps -xc -o命令看起来像这样:

iChat
iChatAgent
MarsEdit
Dictionary
Preview
TextEdit

也就是说,只是流程名称,每行一个,不多,不少现在我们可以使用这样一个简单的模式grep一个特定的应用程序名称:

ps -xc -o command | egrep -c "^iTunes$"

这既聪明又强大。

基准

So which technique is best? I’m pretty sure they’re all equally reliable结果他们都很快。

如果您正在编写AppleScript,答案似乎很明确:使用系统事件,并通过名称或创建者类型识别应用程序我见过有人打电话给PS使用AppleScript的做shell脚本命令,但我不明白为什么除非他们不知道如何使用系统事件做到这一点。

如果你使用的是Perl,答案就不那么明确了,但在我看来,这个问题PS-和-egrep的上面讨论的方法可能是最好的,然后使用苹果::进程这是一个版本的is_app_running()通过调用来工作的Perl子例程PSegrep的

sub is_app_running {
    my $app_name = quotemeta shift;
    my $running = qx( ps -xc -o command | egrep -c "^$app_name\$" );
    chomp $running;
    return $running;
}

Perl的其他选项是苹果::进程,并使用AppleScript通过AppleScript调用系统事件osascript或MacPerl'sDoAppleScript. All seem equally accurate, but thePS-和-grep的技术似乎最快。

我首次尝试对这四种技术进行基准测试(在这里看到来源)没有定论实际上,根据该脚本的结果,呼唤PSegrep的实际上是轻微的慢点比在Perl中使用AppleScript的两种技术中的任何一种。

这对我来说似乎不对 - 我期待着PS-和-egrep的技术至少要比AppleScript调用方法更快,如果不是更快所以我把它插入我的工作副本BBColors而且,瞧,它感觉更加快速,并且使用了时间测量单个调用的工具显示在使用时PS-和-egrep的测试BBEdit是否正在运行,bbcolors每次都跑不到半秒钟使用我之前调用AppleScript的技术,每次通常需要1.5到2.5秒。

在我的基准测试脚本(显示在Perl中调用AppleScript的那个脚本)中,我正在测试每个例程1000次我怀疑发生的事情是有一些严重的缓存和“一切都已经加载到内存中“重复电话的好处但我的真实世界用法根本不像基准测试设置 -bbcolors工具只调用一次这个例程,而不是快速连续调用几百次,当你只调用一次时,基于AppleScript的方法就慢了,因为他们必须编译嵌入式AppleScript代码并调用OSA运行时。

运用苹果::进程在实际使用中,它比基于AppleScript的技术明显更快,但速度并不快PS-和-egrep的


  1. 有关iTunesHelper名称的一些有趣内容在大多数地方,它显示为“iTunesHelper”,没有空间但在Activity Viewer中,它显示为“iTunes Helper”,带有空格AppleScript seems to know about both spellings, but “iTunesHelper” seems to be the canonical one; if you type告诉app“iTunes Helper”激活,在脚本编辑器编译后,应用程序名称更改为“iTunesHelper”我最好的猜测是为什么这是:在iTunesHelper.app/Contents/Info.plistfile, the value for the CFBundleName key is “iTunes Helper”; but the application binary itself is named “iTunesHelper”. ↩︎

  2. With most command-line tools, single-character options can be concatenated together; “ps -xc“相当于”ps -x -c”. ↩︎

  3. 你看过源代码吗?降价↩︎