使用JXA AppleScript获取活动Finder窗口的POSIX路径

时间:2017-07-31 22:26:35

标签: applescript macos-sierra finder jxa

我希望JXA等同于这个AppleScript片段:

tell application "Finder"

    # Get path
    set currentTarget to target of window 1
    set posixPath to (POSIX path of (currentTarget as alias))

    # Show dialog
    display dialog posixPath buttons {"OK"}

end tell

我最接近的是使用url属性初始化Foundation NSURL对象并访问其fileSystemRepresentation属性,如下所示:

// Get path
var finder = Application('Finder')
var currentTarget = finder.finderWindows[0].target()
var fileURLString = currentTarget.url()

// I'd like to get rid of this step
var fileURL = $.NSURL.alloc.initWithString(fileURLString)
var posixPath = fileURL.fileSystemRepresentation

// Show dialog
finder.includeStandardAdditions = true
finder.displayAlert('', {buttons: ['Ok'], message: posixPath})

但这似乎不必要地复杂。是否有更好的方式到达POSIX路径而不使用Foundation API 或手动字符串争论?

System Events AppleScript Dictionary

如果我天真地尝试这个:

finder.finderWindows[0].target().posixPath()

我收到此错误:

app.startupDisk.folders.byName("Users").folders.byName("kymer").folders.byName("Desktop").posixPath()
        --> Error -1728: Can't get object.

SO answer似乎相关,但我似乎无法根据自己的需要对其进行调整:

App = Application.currentApplication()
App.includeStandardAdditions = true
SystemEvents = Application('System Events')

var pathToMe = App.pathTo(this)
var containerPOSIXPath = SystemEvents.files[pathToMe.toString()].container().posixPath()

非常感谢任何帮助!

5 个答案:

答案 0 :(得分:1)

理论上你会写一些类似的东西:

finder.finderWindows[0].target({as:"alias"})

但这不起作用,文档中没有任何内容表明它受支持。但这就是JXA的SOP,就像Apple早期的Scripting Bridge一样,它遭受了许多设计缺陷和遗漏,而这些设计缺陷和遗漏从未(也可能永远不会)修复。[1]

FWIW,以下是使用NodeAutomation在Node.js中执行此操作的方法:

$ node
> Object.assign(this,require('nodeautomation'));undefined
> const fn = app('Finder')
> var file = fn.FinderWindows[0].target({asType:k.alias}) // returns File object
> file.toString() // converts File object to POSIX path string
'/Users/jsmith/dev/nodeautomation'

(请注意,NodeAutomation对我来说是一个非常低优先级的项目,因为Mac自动化看起来几乎是苹果公司的最后一步.Caveat emptor等。对于非平凡的脚本,我强烈建议坚持使用AppleScript因为它是唯一正式支持的解决方案,实际上正常工作。)

[1]例如,另一个JXA限制是大多数应用程序的moveduplicate命令严重受损,因为JXA作者忘记实施insertion reference forms。 (顺便说一下,我在JXA发布之前报告了所有这些问题,而且appscript已经在十年前解决了所有这些问题,所以他们也没有理由不能把它弄好。)

答案 1 :(得分:1)

这样一个简单的AppleScript代码片段没有直接的JXA翻译这一事实证明了基于OSA脚本编写的JXA和macOS自动化的遗憾状态

正如您自己的示例所示,在两种垂死的自动化脚本语言 AppleScript中 - 尽管存在各种瑕疵 - 是更成熟,更可靠的选择

要解决您在JXA中遇到的问题,您自己可能会想出最好的方法。 让我把它打包成一个帮助函数,或许可以轻松地解决这个问题 - 要明确:这样的辅助函数不应该是必需的

// Helper function: Given a Finder window, returns its folder's POSIX path.
// Note: No need for an ObjC.import() statement, because NSURL is 
//       a Foundation class, and all Foundation classes are implicitly
//       available.
function posixPath(finderWin) {
  return $.NSURL.alloc.initWithString(finderWin.target.url()).fileSystemRepresentation
}

// Get POSIX path of Finder's frontmost window:
posixPath(Application('Finder').finderWindows[0])

答案 2 :(得分:0)

@Kymer,你说:

  

但这似乎不必要地复杂。有没有更好的方式去   没有使用Cocoa API或手动字符串争论的POSIX路径?

你走在正确的轨道上。这是我所知道的最好的方法。如果有更好的,我也想了解它们。但是,这似乎运行得很快,适用于文件和文件夹。

var finderApp = Application("Finder");
var itemList  = finderApp.selection();
var oItem      = itemList[0];
var oItemPaths  = getPathInfo(oItem);

/* --- oItemPaths Object Keys ---
  oItemPaths.itemClass
  oItemPaths.fullPath
  oItemPaths.parentPath
  oItemPaths.itemName
*/

console.log(JSON.stringify(oItemPaths, undefined, 4))

function getPathInfo(pFinderItem) {

  var itemClass  = pFinderItem.class();  // returns "folder" if item is a folder.
  var itemURL = pFinderItem.url();
  var fullPath  = decodeURI(itemURL).slice(7);

  //--- Remove Trailing "/", if any, to handle folder item ---
  var pathElem  = fullPath.replace(/\/$/,"").split('/')

  var  itemName   = pathElem.pop();
  var parentPath = pathElem.join('/');

  return {
    itemClass:   itemClass,
    fullPath:    fullPath,
    parentPath:  parentPath,
    itemName:    itemName
    };

}

答案 3 :(得分:0)

这是一个相当简单的示例函数,它只抓取窗口target,然后从file://中删除前导url

/*
pathToFrontWindow()
returns path to front Finder window
*/
function pathToFrontWindow() {
    if ( finder.windows.length ) {
        return decodeURI( finder.windows[0].target().url().slice(7) )
    } else {
        return ""
    }
}

答案 4 :(得分:0)

(() => {

    // getFinderDirectory :: () -> String
    const getFinderDirectory = () =>
        Application('Finder')
        .insertionLocation()
        .url()
        .slice(7);

    return getFinderDirectory();
})();