51

考虑以下 AppleScript:

on is_running(appName)
    tell application "System Events" to (name of processes) contains appName
end is_running

set safRunning to is_running("Safari")
if safRunning then
    tell application "Safari"
      -- Stuff I only want executed if Safari is running goes here.
    end tell
    return "Running"
else
    return "Not running"
end if

问题:当我通过osascript命令行实用程序运行它时,如果 Safari 没有运行,它会启动并且脚本报告“正在运行”。这不是我想要或期望的行为。请注意,在 AppleScript 编辑器中运行时,它可以按预期/预期工作。

这是一个osascript错误/已知问题吗?还是出于我失踪的原因,它是某种有意的行为?任何人都可以让它按要求工作吗?(顺便说一句,我正在运行 OSX 10.7.5;我看不到如何osascript报告版本号)。

如果您注释掉tell/end tell行,它的行为与我预期的一样:如果 Safari 没有运行,它不会启动它,并打印“未运行”。所以在我看来,这似乎tell是导致 Safari 启动的原因,但它不需要实际执行,只需存在于脚本中......?有一段时间我想知道这tell是否应该是这样工作的,但由于它在 AppleScript 编辑器中不能这样工作,我猜不是......

事实上,这是另一个具有类似行为的更疯狂的版本:

on is_running(appName)
    tell application "System Events" to (name of processes) contains appName
end is_running

set safRunning to is_running("Safari")
return safRunning
if false then
    tell application "Safari"
    end tell
end if

这仍然总是启动 Safari,即使在 return 语句之后的块内tellif false(但同样,这在 AppleScript 编辑器中很好。)

顺便说一句,这种行为不仅限于 Safari,而且也不是通用的:

  • 受影响的应用程序包括:Safari、TextEdit、iPhoto、AppleScript Editor、iTerm、...
  • 不受影响的应用程序包括:谷歌浏览器、iTunes、预览、邮件、终端、地址簿、Echofon、...

那么,有人对我如何解决或绕过这个有任何想法吗?这是一个osascript错误吗?还是我错过了有关 AppleScript 语义的内容?

对于上下文:我正在尝试编写一个脚本(要嵌入/从某些 python 调用),它会在打开的浏览器中查询它们打开的任何选项卡的 URL;我一切正常,除了它总是启动 Safari,无论它是否打开。我已经将这种不良行为归结为上面显示的简单测试用例。我不知道有任何方法可以在不使用的情况下从 python 运行此脚本osascript,除了appscript 之外,我不想使用它,因为它不再是开发/支持/推荐的。

非常感谢所有输入/见解!

4

6 回答 6

62

我怀疑你得到这个的原因是因为每次你从命令行使用 osascript 调用脚本时,脚本正在被编译。

在tell应用程序上编译的行为将使应用程序启动。

使用预编译文件中的 osascript 从命令行调用脚本,即 . scpt不会导致此行为,因为无需进行编译。

但是从纯文本 (.txt,.sh ) 文件中调用它会启动应用程序。

如果您不想使用 .scpt 文件并想使用纯文本文件,那么您可以尝试将运行脚本命令放入 applescript 的技巧。

on is_running(appName)
    tell application "System Events" to (name of processes) contains appName
end is_running

set safRunning to is_running("Safari")
if safRunning then
    run script "tell application \"Safari\" 
    
open location \"http://google.com\"
     
    end tell"
    return "Running"
else
    return "Not running"
end if

运行脚本中的脚本仅在需要时编译。您将需要转义任何字符,如我的示例中的引号。

如果您先在普通的 applescript 文档中编写脚本并对其进行编译以检查错误,则会更容易。

然后将其复制到纯文本文件中。


更新**

我上面使用的方法来自一个旧脚本,在我在这里回答之前我曾经用来解决这个问题。

答案有效,而不是试图优雅。;-)

我实际上喜欢下面的user1804762方法。因为它确实有效,但感觉答案不够清楚,所以我将举一个使用它的例子。

set appName to "Safari"

if application appName is running then
    
    tell application id (id of application appName)
        
        open location "http://google.com"
    end tell
    return "Running"
else
    return "Not running"
end if

可以使用 osascript从命令行运行此脚本

例子:

osascript /Users/USERNAME/Desktop/foo.scpt

请注意,该脚本被保存为已编译的脚本。这可以正常工作,您也可以将其保存并用作纯文本脚本。

IE

osascript /Users/USERNAME/Desktop/foo.applescript

于 2013-04-17T23:14:33.093 回答
58

一些信息:

“增强的应用程序对象模型”:

tell application "iTunes"
    if it is running then
      pause
    end if
end tell

你也可以这样做:

if application "iTunes" is running then
    tell application "iTunes" to quit
end if

你也可以这样做:

get name of application "iTunes"
get version of application "iTunes"

并完成旅程:

get id of application "TextEdit" --> "com.apple.TextEdit"
tell application id "com.apple.TextEdit"
    make new document
end tell

那就是“增强的应用程序对象模型”。如果一个应用程序仍然启动(例如,您第一次编译和执行脚本),我认为这是因为 AS 必须从应用程序中获取一些它在字典中找不到的信息(或类似的东西......? )。

于 2013-04-17T20:48:47.333 回答
8

好的,我知道这个问题真的很老了,但我偶然发现它正在寻找一个不同的问题,并且不得不考虑其中一些响应有多复杂。

实现你想要的简单代码是:

tell application "System Events"
  if application process "Safari" exists then
    -- do stuff you want to do only if Safari exists
  end if
end tell

在旧系统上,语法曾经是:

tell application "System Events"
  if exists of application process "Safari" is true then
    -- do stuff you want to do only if Safari exists
  end if
end tell

其中之一绝对适合您,Applescript 解决方案的勇敢搜索者,仅在应用程序运行时才采取行动。


哦!额外提示:如果您不确定应用程序进程名称到底是什么(通常但不总是应用程序名称),在编写最终脚本运行之前……</p>

tell application "System Events"
   get every application process
end tell

并在结果中找到您的应用进程名称。

这是运行该命令的屏幕截图。(注意数以万计的Google Chrome Helper实例。感谢 Google!)

在此处输入图像描述

于 2015-06-23T07:17:12.137 回答
2

但是,所有先前做出的答案都存在相同的问题:

他们通过名称查找应用程序。但是,用户可能会重命名应用程序,然后脚本会认为应用程序没有运行,而实际上它确实运行了。

要正确检查正在运行的应用程序,应通过其捆绑 ID 找到它,用户无法更改该 ID。

可以使用此命令查询捆绑 ID,例如,当应用程序已经运行时:

tell application "System Events"
    get bundle identifier of application process "Safari"
end tell

或者对于任何已安装的应用程序都是这样:

get id of application "Safari"

要检查具有特定捆绑 ID 的应用程序是否正在运行,请使用以下代码:

tell application "System Events"
    set ids to bundle identifier of every application process
    if ids contains "com.apple.safari" then
        return "Running"
    else
        return "Not running"
    end if
end tell

此外,这是一个检查应用程序是否正在运行的示例,然后退出它,然后重新启动它,确保重新启动之前运行的同一个应用程序,而不是其他可能存在的副本:

set bundleID to "com.apple.safari"
set apps to runningApps(bundleID)
set appCount to length of apps
if appCount is not 0 then
    quit application id bundleID
    repeat while length of runningApps(bundleID) = appCount
        -- wait for the app to quit
    end repeat
    open first item of apps
end if

on runningApps(bundleID)
    -- The try block is to catch the rare case of having more than one
    -- copy of an app running at the same time. Unfortunately, in that
    -- case this code will not run as expected, because we don't get the
    -- correct list of multiple items back then. But at least the script
    -- will not crash from it but handle it gracefully.
    tell application "System Events"
        try
            return application file of (every application process whose bundle identifier = bundleID)
        end try
    end tell
    return {}
end runningApps
于 2016-08-12T18:31:46.370 回答
1
tell application "Finder"
    set applicationsnames to get the name of every process whose visible is true
end tell

set appName to "Safari"

if applicationsnames does not contain appName then
    say (appName & " is not running")
    --add here what you want to happen

end if
return applicationsnames

这是{"Finder", "JavaAppLauncher", "firefox", "Microsoft Word", "iTunes", "AppleScript Editor"}为我返回

希望这可以帮助

于 2016-01-21T13:19:43.563 回答
0

我遇到了与此处描述的相同的问题,尝试设置播放/暂停 VLC 或 iTunes 的 AppleScript(由 BetterTouchTool 手势触发),但如果 VLC 未运行(由于我的工作流程),则只有 iTunes,当然,只有 VLC 而它正在运行。(我在 VLC 的设置中使用 iTunes 的自动暂停/播放触发器来启动和退出应用程序。)

VLC 总是在每次重新启动 BTT 后首次使用 BetterTouchTool 触发器时启动,因为此时字典缓存被删除,并且 AppleScript 处理程序必须启动每个脚本应用程序,如果一个指令针对它以便调用它字典。

我没有找到任何可以避免这种情况的东西。有一些尝试,但没有一个对我有用,因为脚本处理程序的字典调用是我们无法影响的。我想出了这个肮脏的解决方法:

  • 创建一个单独的 AppleScript 文件,仅包含包含 VLC 的告诉的行
  • 把它保存在不会惹恼你的地方
  • 将原始 AppleScript 中包含 tell 的行替换为运行该脚本的行

这将导致脚本的第一次编译不直接调用应用程序(在我的例子中是 VLC),只调用脚本,这意味着应用程序不需要启动。

一旦调用了该单独的文件,VLC 将需要启动,但是,如果您调用该文件以告诉 VLC 某些内容,那么无论如何您都已经打开了 VLC(或希望它打开)。

我通过 BetterTouchTool 触发器(在我的例子中是触控板上的特定点击)调用的 AppleScript 如下所示:

if application "iTunes" is running and not application "VLC" is running then
tell application "iTunes" to playpause

如果应用程序“VLC”正在运行则结束然后运行脚本“/Users/jannis/bin/PlayVLC.scpt”结束如果

单独的 AppleScript 文件(“PLayVLC.scpt,保存在我多年前为此目的手动创建的用户文件夹中名为“bin”的文件夹中)就是这样的:

tell application "VLC" to play

如果您手动打开该脚本,它当然也会启动 VLC。但这希望不会经常或永远不需要。

我实际上不知道这是否会产生任何我不知道的更深层次的问题,因为我不是专业编码器;如果是这样,请通知我。我希望这对任何人都有帮助!

于 2018-02-24T17:42:53.307 回答