如何通过osascript处理同一应用程序的两个实例

How can one address two instances of the same application through osascript

本文关键字:实例 两个 应用程序 osascript 何通过 处理      更新时间:2023-09-26

有人能想出一个解决方案来解决osascript按名称索引瓶颈对同一应用程序的多个实例的引用吗?

如果我们获得两个进程id(同一应用程序的两个不同实例各一个),osascript将返回同一实例以换取其中一个pid,就好像它首先将pid映射到应用程序名称,然后检索具有该名称的第一个应用程序进程一样。

例如,启动VLC.app的两个不同实例,播放两个不同的视频文件,类似于:

open -na /Applications/VLC.app ~/fileA.m4v
open -na /Applications/VLC.app ~/fileB.m4v

然后获得两个独立的应用程序进程id,例如:

echo "$(ps -ceo pid=,comm= | awk '/VLC/ { print $1}')"

然后,我们可以使用Applescript或Yosemite JXA Javascript从任一pid获取对应用程序对象的引用。

然而,事实证明,无论我们提供哪个进程id,我们总是返回对同一实例的引用,运行同一视频文件,就好像osascript只是将pid转换为应用程序名称,然后总是返回与该名称匹配的第一个进程一样。

Yosemite Javascript应用程序:

function run() {
    var app = Application.currentApplication();
    app.includeStandardAdditions = true;
    var lstVLC = app.doShellScript(
            "echo '"$(ps -ceo pid=,comm= | awk '/VLC/ { print $1}')'""
        ).split(/['r'n]/).map(Number).map(Application);
    return {
        firstInstance: lstVLC[0].windows[0].name(),
        secondInstance: lstVLC[1].windows[0].name()
    };
}

Applescript:

on run {}
    set strCMD to "echo '"$(ps -ceo pid=,comm= | awk '/VLC/ { print $1}')'""
    set lstNum to paragraphs of (do shell script strCMD)
    repeat with i from 1 to length of lstNum
        set item i of lstNum to (item i of lstNum) as number
    end repeat

    tell application "System Events"
        set oProcA to first application process where unix id = (item 1 of lstNum)
        set oProcB to first application process where unix id = (item 2 of lstNum)
    end tell
    return [name of first window of oProcA, name of first window of oProcB]
end run

对分别为每个实例编写脚本的方法有什么想法吗?

对于每个实例,从与特定进程相同的行中询问窗口的名称,如下所示:

set windowNames to {}
set lstNum to paragraphs of (do shell script "ps -ceo pid=,comm= | awk '/VLC/ { print $1}'")
tell application "System Events" to repeat with i in lstNum
    set end of windowNames to name of first window of (first application process where unix id = i)
end repeat
return windowNames

这在El Capitan中似乎已经修复,因为您的JavaScript代码在我的机器上运行良好。

在Javascript中使用jackjr300的方法,至少可以实现UI脚本(尽管不是应用程序对象接口):

function run() {
    var appSE = Application("System Events");
    app = Application.currentApplication();
    app.includeStandardAdditions = true;
    function uiWidgets(lngID) {
        return appSE.processes.whose({
            unixId: lngID
        })[0].windows[0].uiElements();
    }
    var lstWidgets = app.doShellScript(
            "ps -ceo pid=,comm= | awk '/VLC/ { print $1}'"
        ).split(/'r/).map(Number).map(uiWidgets);
    return lstWidgets;
}

JXA是一堆错误和有缺陷的设计。它没能做到这一点令人沮丧,但完全不足为奇(AS团队有状态)。

至于AppleScript,它从未提供过通过PID直接定位应用程序的方法。在过去,我可能通过启用远程Apple事件并使用eppc://USER@HOST/APPNAME?pid=PID URL定位进程来欺骗它,但现在在10.10上尝试它,如果我能让它工作的话,那就该死了,因为它总是返回"不允许远程访问"错误。

Appscript可以在睡眠中完成这项工作,但我放弃了公众对它的支持,因为苹果的碳战争和糟糕的"替代"Cocoa API迫使它进入"遗留"状态,所以你在那里只能靠自己了。

官方支持的一个可行的选项是OSX的ScriptingBridge框架,它提供了一种通过PID确定进程目标的方法。尽管和JXA一样,YMWV也充满了设计缺陷、功能缺失和应用程序兼容性问题。