如何获取最近打开的文件列表?

Posted

技术标签:

【中文标题】如何获取最近打开的文件列表?【英文标题】:How to get a list of recently opened files? 【发布时间】:2013-01-01 02:38:01 【问题描述】:

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

我尝试了以下方法:

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

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

【问题讨论】:

这是一个 AppleScriptObjC 应用程序、AppleScript Studio 应用程序,还是只是保存为应用程序的普通 AppleScript(使用 AppleScript Editor 创建)? (如果您使用 Xcode 并选择 New Project > Application > Cocoa-AppleScript Application,它会创建一个 AppleScriptObjC 应用程序)。 这是一个使用 AppleScript Editor 创建的普通 AppleScript 应用程序。 This MacScripter thread 有一个从 com.apple.recentitems.plist 获取路径的处理程序。我无法让它在 10.8 上运行。 【参考方案1】:

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

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

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

所以,我在 Xcode 中创建了一个新的“Foundation Tool”项目,它使用了以下代码:

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 项目的结果是一个名为 recentItemsFinagler 的“Unix 可执行文件”,而不是一个应用程序包。

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

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

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

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

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

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 value 是 AppleScript 等效于 Objective-C 的nil)。

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

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

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

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

示例项目:RecentItemsFinagler.zip

【讨论】:

【参考方案2】:

AppleScript

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

目标-c

对于非沙盒应用,您使用LSSharedFileList API -- 这是最合适的方式。

@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 苹果可能不会让你这样做!)

【讨论】:

谢谢!这很棒!你能告诉我如何获得文件的路径吗?我刚刚尝试调查LSSharedFileListItemResolve,但这对我来说没有任何意义。 此外,如果您能告诉我如何将所有这些路径写入文本文件,那将是非常棒的。干杯!【参考方案3】:

我知道回答我自己的问题有点讽刺,但感谢 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

【讨论】:

酷,编辑问题。这不仅仅是获取最近打开的文件列表

以上是关于如何获取最近打开的文件列表?的主要内容,如果未能解决你的问题,请参考以下文章

如何从为目录列表打开的目录中获取文件?

如何在 C# 中按进程获取打开文件句柄的列表?

如何获取某个目录下应用实例期间新创建的文件列表?

Unix命令行:如何获取最近30天修改文件的总大小

(J2ME) 如何在我的 jar 应用程序中获取文件/资源​​列表

我们如何使用 python boto3 获取挂载到 EC2 实例的所有文件系统(EFS)的列表?