Как получить список недавно открытых файлов?

В настоящее время я нахожусь в процессе создания приложения AppleScript. (Обратите внимание, что это простое "приложение" AppleScript, созданное с помощью редактора AppleScript для создания AppleScript и последующего сохранения этого сценария в качестве приложения). Для этого приложения мне нужно знать пути к файлам недавно открытых файлов для текущего пользователя. До сих пор я пробовал много разных методов, но, похоже, ни один из них не дает мне ту информацию, которая мне нужна.

Я пробовал следующее:

  • Я попробовал com.apple.recentitems.plist файл, однако информация о пути к файлу, содержащаяся в этом файле, представляется в формате Hex, который при преобразовании в ASCII полон большого количества mumbo-jumbo, а формат mumbo-jumbo, похоже, меняется для разных файлов и на разных компьютерах.,
  • Я пытался использовать Unix find Команда для фильтрации файлов, открытых за прошедший день, однако здесь я могу выбирать только между недавно измененными и недавно доступными, но не недавно открытыми. Недавно измененные файлы не будут включать открытые файлы, которые не были изменены (например, изображение, которое открывается, но не редактировалось), а файлы, к которым недавно обращались, отображают все файлы, которые были видны в Finder в виде эскизов, даже если они не отображались. не был открыт пользователем.
  • Я пытался рискнуть в Objective-C и LaunchServices/LSSharedFileList.h файл для получения kLSSharedFileListRecentDocumentItems (аналогично этому ответу), однако я никогда раньше не использовал Objective-C, поэтому я не смог заставить что-либо работать с этим должным образом.

Буду очень признателен за любую помощь, которую кто-нибудь может оказать, чтобы помочь мне получить список недавно открытых файлов для текущего пользователя. Кроме того, любая помощь с возможностью записи списка недавно измененных файлов в текстовый файл была бы полезной.

3 ответа

Решение

Ваше решение, вероятно, работает, но, возможно, то, что я придумал, могло бы быть более работоспособным.

Из предложенных 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 является один "исполняемый файл Unix" с именем recentItemsFinagler вместо пакета приложений.

Чтобы использовать этот встроенный исполняемый файл в AppleScript, сначала необходимо убедиться, что сценарий сохранен в виде набора сценариев или приложения, а затем Bundle Contents Элемент панели инструментов должен быть включен, как показано на рисунке ниже:

введите описание изображения здесь

При щелчке по этому элементу панели инструментов отображается блок с содержимым пакета сценариев или пакета приложения. Чтобы добавить свой пользовательский исполняемый файл, просто перетащите его из Finder, как показано на рисунке ниже:

введите описание изображения здесь

Это скопирует его в комплект скриптов, как показано ниже:

введите описание изображения здесь

Чтобы найти этот исполняемый файл во время выполнения, вы можете использовать path to resource Команда AppleScript, которая является частью StandardAdditions.osax, (Обратите внимание, что в зависимости от того, как вы пытаетесь использовать path to resource команда в вашем скрипте, вы можете столкнуться с ошибками "Ресурс не найден", когда вы запускаете скрипт из AppleScript Editor, а не запускаете его дважды щелкнув по нему в Finder. Если вы столкнулись с этим типом ошибки, сначала убедитесь, что вы сохранили все свои изменения в сценарии, затем выйдите из редактора AppleScript, затем запустите приложение сценария, дважды щелкнув по нему в 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 value AppleScript эквивалентно Objective-C nil).

Если вы не знаете об этом, глобальные свойства в AppleScript похожи на глобальные переменные в других языках с одним важным дополнением: когда вы запускаете скрипт и присваиваете значение свойству, это значение фактически сохраняется в самом файле скрипта, и будет сохраняться до следующего запуска сценария.

Скрипт сначала проверяет, recentItemsFinagler равно missing valueи, если это так, устанавливает его в результат (path to resource "recentItemsFinagler"), который является AppleScript alias ссылка. Это похоже на псевдоним в Finder в том, что он может успешно отслеживать изменения, такие как переименования и перемещение из одной папки в другую. Если бы вместо этого я сохранил ее в виде простой строки, а затем переместил этот комплект приложений AppleScript из папки "Мои документы" на рабочий стол, путь больше не был бы действительным, и свойство должно было бы обновляться при каждом запуске сценария.

Во всяком случае, мы тогда установили recentItemsString к результату AppleScript do shell scriptrecentItemsFinagler инструмент, который является строкой. Чтобы превратить это в список строк AppleScript, представляющих пути, вы используете paragraphs of команда.

Таким образом, включив исполняемый файл в скрипт или в комплект приложения AppleScript, вы можете получить желаемый результат менее чем за 10 строк кода.

Пример проекта: RecentItemsFinagler.zip

AppleScript

для этого не существует API -интерфейса AppleScript (эквивалентного LSSharedFileList).. либо вы вызываете код objC (см. фрагмент objC ниже), либо.. вы просто читаете plist, который является немного хакерским IMO:)

Objective-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

хотяон не работает в песочнице - и реального обходного пути нет (как вы сказали, вы могли бы прочитать этот список напрямую, но... это немного хакерски:D PLUS apple, скорее всего, не позволит вам сделать это!)

Я знаю, что немного иронично отвечать на мой собственный вопрос, но благодаря Дай-Джану и большому количеству поисковиков я нашел свой ответ. Это, без сомнения, не самый чистый способ сделать это, но он делает то, что мне нужно. Я надеюсь, что это может помочь кому-то в будущем!

#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
Другие вопросы по тегам