5

我目前正在构建 AppleScript 应用程序。(请注意,这是一个普通的 AppleScript“应用程序”,通过使用 AppleScript 编辑器创建 AppleScript,然后将该脚本保存为应用程序)。对于这个应用程序,我需要知道当前用户最近打开的文件的文件路径。到目前为止,我已经尝试了许多不同的方法,但似乎都没有给我所需的信息。

我尝试了以下方法:

  • 我试过这个com.apple.recentitems.plist文件,但是这个文件中包含的文件路径信息似乎是十六进制格式,当转换成 ASCII 时充满了很多 mumbo-jumbo,而且 mumbo-jumbo 的格式似乎因不同而改变文件和不同的计算机上。
  • 我尝试使用 unixfind命令过滤过去一天打开的文件,但是在这里我只能在最近修改和最近访问之间进行选择,而不是最近打开。最近修改的文件不包括未修改的打开文件(如打开但未编辑的图像),最近访问的文件似乎显示所有在 Finder 中以缩略图视图可见的文件,即使它们没有t 被用户打开。
  • 我已经尝试过使用 Objective-C 和获取LaunchServices/LSSharedFileList.h文件kLSSharedFileListRecentDocumentItems(类似于这个答案),但是我以前从未使用过 Objective-C,所以我无法让任何东西正常工作。

任何人都可以提供任何帮助来帮助我获取当前用户最近打开的文件的列表,我们将不胜感激。此外,能够将最近更改的文件列表写入文本文件的任何帮助都会很棒。

4

3 回答 3

1

苹果脚本

该 afaik 没有 AppleScript API(相当于 LSSharedFileList).. 要么你调用 objC 代码(参见下面的 objC 代码段),要么.. 你只是阅读了 plist,这有点骇人听闻的 IMO :)

目标-c

对于非沙盒应用程序,您使用LSSharedFileListAPI——这是最合适的方式。

@implementation DDAppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    LSSharedFileListRef list = LSSharedFileListCreate(NULL, kLSSharedFileListRecentDocumentItems, 0);
    CFArrayRef items = LSSharedFileListCopySnapshot(list, NULL);

    for(int i=0; i<CFArrayGetCount(items); i++) {
        LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(items, i);

        //get items properties... for demo I just get name
        NSString *name = (__bridge_transfer NSString*)LSSharedFileListItemCopyDisplayName(item);

        NSLog(@"%@", name);
    }
}

@end

但它在沙盒中不起作用- 并且没有真正的解决方法(正如你所说,你可以直接阅读 plist 但是......这有点骇人听闻:D PLUS苹果可能不会让你这样做!)

于 2013-01-01T03:02:42.810 回答
1

您的解决方案可能有效,但也许我想出的可能更可行。

在您提出的 3 个建议选项中,这些LSSharedFile*命令确实是最好的解决方案。

随着 AppleScript 脚本包 (.scptd) 和可以保存为应用程序包 (.app) 的脚本的出现,您可以很容易地在包中包含自定义可执行文件。因此,如果您无法单独使用 AppleScript 或默认的 BSD 子系统工具来完成它,您可以利用这些可执行文件来完成您需要做的事情。

因此,在 Xcode 中,我创建了一个新的“基础工具”项目,该项目使用以下代码:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        UInt32 resolveFlags = 0;
        NSArray *arguments = [[NSProcessInfo processInfo] arguments];
        if (arguments.count > 1) {
            NSArray *revisedArguments = [arguments
                        subarrayWithRange:NSMakeRange(1, arguments.count - 1)];
            for (NSString *arg in revisedArguments) {
                if ([arg isEqualToString:@"--noUserInteraction"]) {
                    resolveFlags |= kLSSharedFileListNoUserInteraction;
                } else if ([arg isEqualToString:@"--doNotMountVolumes"]) {
                    resolveFlags |= kLSSharedFileListDoNotMountVolumes;
                }
            }
        }
        LSSharedFileListRef sharedFileListRef = LSSharedFileListCreate(NULL,
                              kLSSharedFileListRecentDocumentItems, NULL);
        NSArray *sharedFileListItemRefs =
           (NSArray *)LSSharedFileListCopySnapshot(sharedFileListRef, NULL);
        for (id itemRef in sharedFileListItemRefs) {
            NSURL *resolvedURL = nil;
            LSSharedFileListItemResolve((LSSharedFileListItemRef)itemRef,
                 resolveFlags, (CFURLRef *)&resolvedURL, NULL);
            if (resolvedURL) {
                printf("%s\n", resolvedURL.path.fileSystemRepresentation);
                [resolvedURL release];
            }
        }
        if (sharedFileListRef) CFRelease(sharedFileListRef);
        [sharedFileListItemRefs release];
        return EXIT_SUCCESS;
    }
}

虽然此代码与您的代码相似,但不必将结果写入中间文件,它只是将文件路径打印到标准输出。这应该可以大大简化 AppleScript 端的编码。构建这个 Xcode 项目的结果是一个单独的“Unix 可执行文件” recentItemsFinagler,而不是一个应用程序包。

要在 AppleScript 中使用此构建的可执行文件,您首先需要确保将脚本保存为脚本包或应用程序,然后Bundle Contents应启用工具栏项,如下图所示:

在此处输入图像描述

单击该工具栏项会显示抽屉,其中显示脚本包或应用程序包的内容。要添加您的自定义可执行文件,只需将其从 Finder 拖放,如下图所示:

在此处输入图像描述

这会将其复制到脚本包中,如下所示:

在此处输入图像描述

要在运行时定位此可执行文件,您可以使用path to resourceAppleScript 命令,它是StandardAdditions.osax. (请注意,根据您尝试在脚本中使用该path to resource命令的方式,当您从 AppleScript 编辑器中运行脚本而不是通过在 Finder 中双击它来运行它时,您可能会遇到“找不到资源”错误. 如果您遇到此类错误,首先确保您已保存对脚本的所有更改,然后退出 AppleScript Editor,然后在脚本后在 Finder 中双击它来启动脚本应用程序完成运行,重新打开 AppleScript 编辑器,然后重新打开脚本应用程序,然后尝试从 AppleScript 编辑器中运行,看看它是否有效)。

因此,要recentItemsFinagler在 AppleScript 中使用它,您可以执行以下操作:

property recentItemsFinagler : missing value

if (recentItemsFinagler = missing value) then
    set recentItemsFinagler to (path to resource "recentItemsFinagler")
end if

set toolPath to quoted form of POSIX path of recentItemsFinagler

set recentItemsString to (do shell script toolPath & " --noUserInteraction --doNotMountVolumes")

set recentItemsPaths to paragraphs of recentItemsString

我将逐行浏览这个 AppleScript 来解释它在做什么。我们首先创建一个全局属性recentItemsFinagler,并将其初始值设置为missing value。(missing valueAppleScript 相当于 Objective-C 的nil)。

如果您不知道,AppleScript 中的全局属性就像其他语言中的全局变量一样,还有一个重要的补充:当您运行脚本并为属性赋值时,该值实际上保存在脚本文件本身中,并将持续到您下次运行脚本时。

该脚本首先检查是否recentItemsFinagler等于missing value,如果是,则将其设置为 的结果(path to resource "recentItemsFinagler"),这是一个 AppleScriptalias引用。这类似于 Finder 中的别名,因为它能够成功跟踪重命名等更改,以及从一个文件夹移动到另一个文件夹。如果我将它存储为一个简单的字符串,然后将此 AppleScript 应用程序包从我的 Documents 文件夹移动到我的桌面,则路径将不再有效,并且每次运行脚本时都必须更新该属性。

无论如何,我们然后设置recentItemsString为 AppleScriptdo shell script recentItemsFinagler工具的结果,它是一个字符串。要将其转换为代表路径的字符串的 AppleScript 列表,请使用该paragraphs of命令。

因此,通过将可执行文件包含在脚本或 AppleScript 应用程序的包中,您可以在不到 10 行代码中获得所需的结果。

示例项目:RecentItemsFiagler.zip

于 2013-01-04T01:55:03.043 回答
0

我知道回答我自己的问题有点讽刺,但感谢 Daij-Djan 和大量谷歌搜索,我找到了答案。毫无疑问,这不是最干净的方法,但它可以满足我的需要。我希望这可以帮助将来的人!

#import "AppDelegate.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    LSSharedFileListRef list = LSSharedFileListCreate(NULL, kLSSharedFileListRecentDocumentItems, 0);
    CFArrayRef items = LSSharedFileListCopySnapshot(list, NULL);


    NSArray *paths = NSSearchPathForDirectoriesInDomains
    (NSLibraryDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];

    NSString *fileName = [NSString stringWithFormat:@"%@/Application Support/MyAppName/RecentFiles.txt",documentsDirectory];

    NSString *content = @"";
    [content writeToFile:fileName
              atomically:NO
                encoding:NSStringEncodingConversionAllowLossy
                   error:nil];

    for(int i=0; i<CFArrayGetCount(items); i++) {
        LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(items, i);

        CFURLRef itemURL = NULL;
        LSSharedFileListItemResolve(item, kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes, &itemURL, NULL);

       if (itemURL != (NULL)) {

        NSString *str = [NSString stringWithFormat:@"%@\n", itemURL]; //get text from textField

            NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:fileName];

            // move to the end of the file
            [fileHandle seekToEndOfFile];

            // convert the string to an NSData object
            NSData *textData = [str dataUsingEncoding:NSUTF8StringEncoding];

            // write the data to the end of the file
            [fileHandle writeData:textData];

            // clean up
            [fileHandle closeFile];


             }

    }
    exit(0);
}

@end
于 2013-01-01T09:08:00.167 回答