【问题标题】:How to get POSIX path of the current script's folder in JavaScript for Automation?如何在 JavaScript for Automation 中获取当前脚本文件夹的 POSIX 路径?
【发布时间】:2015-04-30 15:29:24
【问题描述】:

在 AppleScript 中,可以使用以下行获取当前脚本所在文件夹的 POSIX 路径:

POSIX path of ((path to me as text) & "::")

示例结果:/Users/aaron/Git/test/

什么是 JavaScript 等价物?

【问题讨论】:

    标签: macos applescript osx-yosemite osascript javascript-automation


    【解决方案1】:

    这是一种方法[注意:我不再推荐这种方法。请参阅下面的编辑]

    app = Application.currentApplication();
    app.includeStandardAdditions = true;
    path = app.pathTo(this);
    app.doShellScript('dirname \'' + path + '\'') + '/';
    

    注意 path 周围的单引号,以便在 doShellScript 中处理带有空格等的路径

    编辑 在被@foo 拍打后使用相当不安全的路径引用方法,我想修改这个答案:

    ObjC.import("Cocoa");
    app = Application.currentApplication();
    app.includeStandardAdditions = true;
    thePath = app.pathTo(this);
    
    thePathStr = $.NSString.alloc.init;
    thePathStr = $.NSString.alloc.initWithUTF8String(thePath);
    thePathStrDir = (thePathStr.stringByDeletingLastPathComponent);
    
    thePathStrDir.js + "/";
    

    如果你要使用这个字符串,当然,你仍然需要处理它是否包含有问题的字符。但至少在现阶段这不是问题。这也演示了一些可供 JXA 用户使用的概念,例如使用 ObjC 桥和 .js 将字符串“强制”为 JavaScript 字符串(来自 NSString)。

    【讨论】:

    • 不错!我不知道this
    • "注意路径周围的单引号以使用带有空格的路径" ...如果路径字符串包含单引号,则完全爆炸。 永远不要在没有完全清理您的输入的情况下生成代码:这是完全不安全的。使用do shell script 时,始终使用function quotedForm(s) {return "'"+s.replace("'","'\\''")+"'"} 在连接之前正确地单引号您的任意文本,例如app.doShellScript('dirname ' + quotedForm(path)).
    • 谢谢你,@foo - 我已经更改(添加到)我的答案。
    • @foo:很好的提示,但应该是 function quotedForm(s) { return "'" + s.replace(/'/g, "'\\''") + "'" },以确保 所有 嵌入的 ' 实例被“转义”。
    • @mklement0 令人惊讶的是,第一个纠正这个错误的评论是在将近两年后出现的……两秒钟的测试就会发现这个错误。
    【解决方案2】:

    使用-[NSString stringByDeletingLastPathComponent],它已经知道如何安全地移除最后一个路径段:

    ObjC.import('Foundation')
    
    path = ObjC.unwrap($(path).stringByDeletingLastPathComponent)
    

    或者,如果您喜欢更危险的生活,您可以使用正则表达式从 POSIX 路径字符串中删除最后一个路径段。在我的头顶(警告购买者等):

    path = path.replace(/\/[^\/]+\/*$/,'').replace(/^$/,'/')
    

    (请注意,第二个replace() 是正确处理具有

    【讨论】:

      【解决方案3】:

      所以总结一下我现在在做什么,我正在回答我自己的问题。使用@foo 和@CRGreen 的出色回复,我得出以下结论:

      ObjC.import('Foundation');
      var app, path, dir;
      
      app = Application.currentApplication();
      app.includeStandardAdditions = true;
      
      path = app.pathTo(this);
      dir = $.NSString.alloc.initWithUTF8String(path).stringByDeletingLastPathComponent.js + '/';
      

      这与@CRGreen 的响应非常接近,但是更简洁一些,我导入的是Foundation 而不是Cocoa。另外,我声明了我用来避免意外全局变量的变量。

      【讨论】:

      • 我已经阅读了一些关于使用 Foundation 的优点,但也许你可以在这里谈谈你使用它的原因,@aaronk6
      • @CRGreen 我不是 Mac 或 iOS 开发人员,但我天真地假设Foundation 可能小于Cocoa,后者由Foundation KitApplication KitCore Data 和其他人。
      • 干得好; @CRGreen:引用OSX 10.10 JXA release notes 的话:“Foundation 框架中的符号默认情况下在 JavaScript for Automation 中可用”。换句话说:不需要明确的Objc.import('Foundation')。另外,使用包装JS对象的快捷语法,我们可以简化为dir = $(path).stringByDeletingLastPathComponent.js + '/'
      【解决方案4】:

      我想我找到了一种无需调用 ObjC 即可获取父文件夹的更简单方法。

      var app = Application.currentApplication();
      app.includeStandardAdditions = true;
      thePath = app.pathTo(this);
      
      Path(thePath + '/../../')
      

      【讨论】:

      • 很好,但是要获取脚本所在的文件夹,您只需要Path(thePath + '/..')(只有一个级别)。
      • 实际上,路径的规范化(/.. 的分辨率)仅似乎发生,并且似乎是脚本编辑器打印的隐式结果的产物;例如,对于脚本/foo/dir/script,让脚本编辑器打印Path(thePath + '/..') 的结果会隐式显示/foo/dir,但如果您使用console.log 检查值或执行字符串连接,您将得到/foo/dir/script/..,即非规范化路径。
      【解决方案5】:

      不涉及 ObjC 的纯 JXA 代码:

      App = Application.currentApplication()
      App.includeStandardAdditions = true
      SystemEvents = Application('System Events')
      
      var pathToMe = App.pathTo(this)
      var containerPOSIXPath = SystemEvents.files[pathToMe.toString()].container().posixPath()
      

      【讨论】:

      • 为找出一个纯粹的 JXA 方法表示敬意。附带的好处是SystemEvents.files[pathToMe.toString()].container() 使您可以访问具有报告有关文件夹信息的属性的成熟对象。一个潜在的缺点——这对于单个调用可能无关紧要——是这种方法比通过$(App.pathTo(this).toString()).stringByDeletingLastPathComponent.js 使用 ObjC 桥要慢得多(根据我的非正式测试,慢了大约 5-6 倍)。
      【解决方案6】:

      通过提供独立的实用功能来补充有用的现有答案:

      // Return the POSIX path of the folder hosting this script / app.
      // E.g., from within '/foo/bar.scpt', returns '/foo'.
      function myPath() {
          var app = Application.currentApplication(); app.includeStandardAdditions = true
          return $(app.pathTo(this).toString()).stringByDeletingLastPathComponent.js
      }
      
      // Return the filename root (filename w/o extension) of this script / app.
      // E.g., from within '/foo/bar.scpt', returns 'bar'.
      // (Remove `.stringByDeletingPathExtension` if you want to retain the extension.)
      function myName() {
          var app = Application.currentApplication(); app.includeStandardAdditions = true
          return $(app.pathTo(this).toString()).lastPathComponent.stringByDeletingPathExtension.js
      }
      

      注意:这些函数使用 ObjC 桥的快捷语法形式$(...) 用于ObjC.wrap().js 用于ObjC.unwrap(),并且还利用了以下事实: Foundation 框架的符号默认情况下是可用的 - 请参阅 OS X 10.10 JXA release notes


      很容易概括这些函数以提供 POSIX dirnamebasename 实用程序的等效项

      // Returns the parent path of the specified filesystem path.
      // A trailing '/' in the input path is ignored.
      // Equivalent of the POSIX dirname utility.
      // Examples:
      //    dirname('/foo/bar') // -> '/foo'
      //    dirname('/foo/bar/') // ditto
      function dirname(path) {
        return $(path.toString()).stringByDeletingLastPathComponent.js
      }
      
      // Returns the filename component of the specified filesystem path.
      // A trailing '/' in the input path is ignored.
      // If the optional <extToStrip> is specified:
      //   - If it it is a string, it is removed from the result, if it matches at
      //     the end (case-sensitively) - do include the '.'
      //   - Otherwise (Boolean or number), any truthy value causes any extension
      //     (suffix) present to be removed.
      // Equivalent of the POSIX basename utility; the truthy semantics of the
      // 2nd argument are an extension.
      // Examples:
      //    basename('/foo/bar') // -> 'bar'
      //    basename('/foo/bar/') // ditto
      //    basename('/foo/bar.scpt', 1) // -> 'bar'
      //    basename('/foo/bar.scpt', '.scpt') // -> 'bar'
      //    basename('/foo/bar.jxa', '.scpt') // -> 'bar.jxa'
      function basename(path, extToStrip) {
        path = path.toString()
        if (path[path.length-1] === '/') { path = path.slice(0, -1) }
        if (typeof extToStrip === 'string') {
          return path.slice(-extToStrip.length) === extToStrip ? $(path).lastPathComponent.js.slice(0, -extToStrip.length) : $(path).lastPathComponent.js    
        } else { // assumed to be numeric: if truthy, strip any extension
          return extToStrip ? $(path).lastPathComponent.stringByDeletingPathExtension.js : $(path).lastPathComponent.js    
        }
      }
      

      【讨论】:

        【解决方案7】:

        上面有很多有趣的解决方案。

        这是我的,它不需要 ObjC,并返回一个具有可能需要的属性的对象。

        'use strict';
        var oScript = getScriptProp(this);
        
        /*oScript Properties
            Path
            Name
            ParentPath
            Folder
        */
        
        oScript.ParentPath;
        
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        
        function getScriptProp(pRefObject) {
        
          var app = Application.currentApplication()
          app.includeStandardAdditions = true
        
          var pathScript = app.pathTo(pRefObject).toString();
          var pathArr = pathScript.split("/")
        
          var oScript = {
            Path: pathScript,
            Name: pathArr[pathArr.length - 1],
            ParentPath: pathArr.slice(0, pathArr.length - 1).join("/"),
            Folder: pathArr[pathArr.length - 2]
          };
        
          return oScript
        }
        

        【讨论】:

        • app.pathTo(this) 指向脚本编辑器应用程序,而不是脚本包本身。
        猜你喜欢
        • 2012-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-13
        • 2014-01-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多