如何在JavaScript for Automation中获取当前脚本文件夹的POSIX路径?

7

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

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

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

JavaScript的等效代码是什么?

7个回答

9

没有涉及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倍)。 - mklement0

5
这里有一种方法(注:我不再推荐这种方法,请参见下面的编辑):
app = Application.currentApplication();
app.includeStandardAdditions = true;
path = app.pathTo(this);
app.doShellScript('dirname \'' + 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 - aaronk6
3
请注意单引号将路径括起来,以便使用包含空格的路径。如果路径字符串包含单引号,则会完全失败。绝不要在没有充分净化输入的情况下生成代码:这是非常不安全的。在使用“do shell script”时,请始终使用function quotedForm(s) {return "'"+s.replace("'","'\\''")+"'"}正确地给你的任意文本加上单引号,然后再进行连接,例如,app.doShellScript('dirname ' + quotedForm(path)) - foo
@CRGreen,感谢您的修改!很高兴我们已经摆脱了shell调用。我创建了一个稍微改变了的版本的代码 - 请参见我的下面的答案。 - aaronk6
1
@foo:好的提示,但应该是function quotedForm(s) { return "'" + s.replace(/'/g, "'\\''") + "'" },以确保所有嵌入式 ' 实例都被“转义”。 - mklement0
1
@mklement0 令人惊讶的是,第一个指出这个错误的评论竟然在将近两年后才出现... 只需要测试两秒钟就可以发现这个错误。 - BallpointBen
显示剩余3条评论

4
为了补充有帮助的现有答案,提供自包含实用函数:
// 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发行说明

可以将这些函数推广以提供与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    
  }
}

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
1
@CRGreen 我不是Mac或iOS开发人员,但是我的天真假设是 Foundation 可能比 Cocoa 更小,后者包括 Foundation KitApplication KitCore Data等组件。 - aaronk6
1
干得好;@CRGreen:引用自OSX 10.10 JXA发布说明:“Foundation框架中的符号在JavaScript for Automation中默认可用”。换句话说:不需要显式的Objc.import('Foundation')。此外,使用JS对象的快捷语法,我们可以简化为dir = $(path).stringByDeletingLastPathComponent.js + '/' - mklement0

2

我认为我找到了一种不需要调用ObjC的更简单的方法来获取父文件夹。

var app = Application.currentApplication();
app.includeStandardAdditions = true;
thePath = app.pathTo(this);

Path(thePath + '/../../')

不错,但是要获取脚本所在的文件夹,只需要 Path(thePath + '/..')(只向上一级)。 - mklement0
实际上,路径的规范化(解析/..)只是_表面上_发生了,并且似乎是脚本编辑器隐式打印结果的产物;例如,对于脚本/foo/dir/script,让脚本编辑器隐式显示Path(thePath + '/..')的结果会显示/foo/dir,但如果您使用console.log检查该值或执行字符串连接,则会得到/foo/dir/script/..,即非规范化路径。 - mklement0

2
使用-[NSString stringByDeletingLastPathComponent]来安全地删除最后一个路径段:
ObjC.import('Foundation')

path = ObjC.unwrap($(path).stringByDeletingLastPathComponent)

或者,如果你更喜欢冒险的生活,你可以使用正则表达式从POSIX路径字符串中删除最后一个路径段。我想到的方法是(请注意风险等等):

否则,如果您喜欢更危险的生活,您可以使用正则表达式从POSIX路径字符串中剥离最后一个路径段。恕我直言(请自行承担风险等):

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

(请注意,第二个replace()是必需的,以正确处理具有<2个部分的路径。)

-2

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

这是我的解决方案,不需要 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) 指向脚本编辑器应用程序,而不是脚本包本身。 - adib

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接