Получите POSIX-путь активного окна Finder с помощью JXA AppleScript

Мне нужен 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 или обработки строк вручную?

Словарь AppleScript системных событий

Если я наивно попробую это:

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

Я получаю такую ​​ошибку:

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

Этот ответ SO кажется актуальным, но я не могу адаптировать его под свои нужды:

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

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

Любая помощь будет принята с благодарностью!


person Kymer    schedule 31.07.2017    source источник
comment
Что не так с Какао API? Это намного быстрее, чем отправка событий Apple.   -  person vadian    schedule 02.08.2017
comment
@vadian: Это простая задача создания сценариев, которая должна иметь простое решение только для JXA, аналогичное (разумно) простому решению только для AppleScript. Требование от сценаристов прибегать к базовым API (которым в данном случае является Foundation, а не Cocoa, кстати) в таком простом случае является ненужным бременем, которое представляет собой квантовый скачок, которого можно избежать. с точки зрения необходимых знаний.   -  person mklement0    schedule 06.08.2017
comment
@ mklement0 Я знаю, что это Foundation, я только что процитировал фразу из вопроса. Однако вы также предлагаете использовать NSURL в своем ответе.   -  person vadian    schedule 07.08.2017
comment
@vadian: Я предлагаю NSURL, потому что это самое простое и надежное решение с учетом обстоятельств, но, чтобы быть абсолютно ясным: это НЕ должно быть необходимо . Я прояснил это в своем ответе.   -  person mklement0    schedule 07.08.2017
comment
Хороший момент в том, что NSURL является частью Foundation, а не Cocoa. Отредактировал вопрос. В этом нет ничего неправильного, но я надеялся, что в этом не было бы необходимости. Я бы предпочел чистое решение JXA (без использования ручных манипуляций со строками) по причинам, упомянутым mklement0.   -  person Kymer    schedule 07.08.2017


Ответы (4)


Тот факт, что такой простой фрагмент кода AppleScript не имеет прямого перевода JXA, свидетельствует о плачевном состоянии автоматизации JXA и macOS на основе сценариев OSA в целом:

Как показывает ваш собственный пример, среди двух умирающих языков сценариев автоматизации 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])
person mklement0    schedule 06.08.2017
comment
Интересный пост в блоге о состоянии автоматизации, который вы там разместили. Да и спасибо за подсказку о ненужном импорте! - person Kymer; 07.08.2017

Теоретически вы бы написали что-то вроде:

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

но это не работает, и в документации нет ничего, что указывало бы на его поддержку. Но это СОП для JXA, который, как и более ранний мост сценариев Apple, страдает многочисленными конструктивными недостатками и упущениями, которые никогда (и, вероятно, никогда не будут) исправлены. [1]

FWIW, вот как вы это делаете в Node.js, используя NodeAutomation:

$ 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 Automation, похоже, находится на последнем издыхании в Apple. Caveat emptor и т. Д. Для нетривиальных сценариев я настоятельно рекомендую придерживаться AppleScript, поскольку это единственное официально поддерживаемое решение, которое действительно работает правильно.)


[1] Например, еще одним ограничением JXA является то, что команды move и duplicate большинства приложений серьезно повреждены, поскольку авторы JXA забыли реализовать формы для вставки ссылок. (Кстати, я сообщал обо всех этих проблемах еще до того, как был выпущен JXA, а в appscript все это было решено десять лет назад, поэтому у них нет оправдания, что они не поняли это правильно.)

person foo    schedule 02.08.2017
comment
@JMichaelTX: Если это вы голосовали против, пожалуйста, служите интересам OP, а не своим личным интересам. - person foo; 03.08.2017
comment
Голос против был определенно не заслуженным. И если это должно было сделать другие ответы более заметными, это действительно очень мелочь. К сожалению, я не могу полагаться на внешнюю зависимость для моего варианта использования, но, тем не менее, ваш ответ полезен. Вы, кажется, очень хорошо разбираетесь в автоматизации. Вы рассматривали возможность размещения своего репо на Github? Могут стать более заметными, и я обязательно буду отмечать их там или следить за ними :) - person Kymer; 04.08.2017
comment
О наглядности: написание NodeAutomation было больше для доказательства правильности, чем для создания новой платформы для автоматизации Mac. Мнение среди тех, кто обращает внимание, заключается в том, что AppleScript и IPC для мероприятий Apple наконец-то уходят в Apple после многих лет бесхозяйственности, поэтому я не призываю кого-либо бросаться на подножку xAutomation. Был там, сделал это и сжег более 1000 пользователей appscript за их проблемы. Если другие хотят использовать [Swift / Node] Automation и работать с ними, они могут попробовать; лично я представляю их только как полезную точку зрения. - person foo; 04.08.2017

Вот довольно простой пример функции, которая просто захватывает 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 ""
    }
}
person Patrick Wynne    schedule 03.08.2017
comment
Хотя это может дать ответ на вопрос, пожалуйста, также добавьте краткое объяснение того, что на самом деле делает код и почему он решает исходную проблему. - person user1438038; 04.08.2017
comment
Я действительно хотел избежать нарезки и нарезки струн. Надеялся каким-то образом напрямую завладеть свойством posixPath. - person Kymer; 04.08.2017
comment
Проблема в том, что свойство target класса window Finder возвращает спецификатор класса folder, который не имеет свойства posixPath. Вам нужен объект alias, чтобы получить posixPath, и, насколько я могу судить, нет способа преобразовать folder в alias в JXA. - person Patrick Wynne; 05.08.2017

@Kymer, вы сказали:

Но это кажется излишне сложным. Есть ли более лучший способ добраться до пути POSIX без использования Какао API или ручного преобразования строк?

Вы на правильном пути. Вот лучший из известных мне методов. Если есть лучше, я тоже хотел бы о них узнать. Но, похоже, это работает так же быстро и работает как для файлов, так и для папок.

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
    };

}
person JMichaelTX    schedule 01.08.2017
comment
Спасибо, что нашли время ответить. Я знаю, что могу вручную обработать строку пути, но это то, чего я действительно хотел избежать. Надеялся каким-то образом получить свойство posixPath. - person Kymer; 04.08.2017