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

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

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

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

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


person sebthedev    schedule 01.01.2013    source источник
comment
Является ли это приложением AppleScriptObjC, приложением AppleScript Studio или просто простым AppleScript (созданным с помощью редактора AppleScript Editor), сохраненным как приложение? (Если вы используете Xcode и выбираете New Project > Application > Cocoa-AppleScript Application, создается приложение AppleScriptObjC).   -  person NSGod    schedule 01.01.2013
comment
Это простое приложение AppleScript, созданное с помощью редактора AppleScript.   -  person sebthedev    schedule 01.01.2013
comment
В этом потоке MacScripter есть обработчик для получения путей из com.apple.recentitems.plist . Я не мог заставить его работать на 10.8, хотя.   -  person Lri    schedule 01.01.2013


Ответы (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, а не с помощью двойного щелчка по нему в диалоговом окне. 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 nil в Objective-C).

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

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

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

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

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

person NSGod    schedule 04.01.2013

AppleScript

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

цель-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, скорее всего, не позволит вам сделать это!)

person Daij-Djan    schedule 01.01.2013
comment
Спасибо! Это здорово! Подскажите, пожалуйста, как узнать путь к файлу? Я только что пытался заглянуть в LSSharedFileListItemResolve, но для меня это не имеет никакого смысла. - person sebthedev; 01.01.2013
comment
Кроме того, если бы вы могли рассказать мне, как я могу записать все эти пути в текстовый файл, это было бы совершенно фантастически. Ваше здоровье! - person sebthedev; 01.01.2013

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

#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
person sebthedev    schedule 01.01.2013
comment
круто, отредактируйте вопрос, хотя. это не просто получение списка недавно открытых файлов - person Daij-Djan; 01.01.2013