Как получить список недавно открытых файлов?
В настоящее время я нахожусь в процессе создания приложения 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 script
recentItemsFinagler
инструмент, который является строкой. Чтобы превратить это в список строк 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