If you use OS X’s file coordination APIs to coordinate access to the file, any well-behaved OS X app (including OmniOutliner, of course!) should automatically notice the changes you’ve made and refresh any open copies of the document. (This is how Time Machine and iCloud Drive and OmniPresence work.)
For those times when it isn’t convenient to modify the tool to call those APIs directly, I’ve written this little UNIX tool which can coordinate access to another file while running another UNIX command.
// Copyright 2016 Omni Development, Inc. All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// <http://www.omnigroup.com/developer/sourcecode/sourcelicense/>.
//
// $Header: svn+ssh://source.omnigroup.com/Source/svn/Omni/trunk/Staff/kc/file-coord/file-coord.m 253856 2016-01-28 15:01:15Z kc $
//
// Usage: ./file-coord path -- command [arguments]
// Example usage: ./file-coord /tmp/test-coord.oo3 -- /bin/zsh -c 'sed "s/Hello.*</Hello, world</" < /tmp/test-coord.oo3/contents.xml > /tmp/new-contents.xml; mv /tmp/new-contents.xml /tmp/test-coord.oo3/contents.xml'
// Build with: cc -o file-coord -Wall -fobjc-arc file-coord.m -framework Foundation
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
__block int taskTerminationStatus = 0;
@autoreleasepool {
NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] init];
NSMutableArray *fileURLs = [NSMutableArray array];
NSMutableArray *toolArguments = [NSMutableArray array];
BOOL scanningToolArguments;
for (int argIndex = 1; argIndex < argc; argIndex++) {
NSString *argument = [NSString stringWithUTF8String:argv[argIndex]];
if (scanningToolArguments) {
[toolArguments addObject:argument];
} else if ([argument isEqualToString:@"--"]) {
scanningToolArguments = YES;
} else {
[fileURLs addObject:[NSURL fileURLWithPath:argument]];
}
}
if ([fileURLs count] != 1 || [toolArguments count] < 1) {
fprintf(stderr, "Usage: %s path -- command [arguments]\n", argv[0]);
NSLog(@"fileURLs=%@, toolArguments=%@", fileURLs, toolArguments);
exit(1);
}
NSURL *fileURL = [fileURLs objectAtIndex:0]; // We've already tested to make sure there's one and only one
NSError *fileCoordinatorError = nil;
[fileCoordinator coordinateReadingItemAtURL:fileURL options:0 writingItemAtURL:fileURL options:0 error:&fileCoordinatorError byAccessor:^(NSURL *newReadingURL, NSURL *newWritingURL) {
if (![newReadingURL isEqual:fileURL]) {
NSLog(@"Aborting: file moved to %@", newReadingURL);
exit(1);
}
if (![newWritingURL isEqual:fileURL]) {
NSLog(@"Aborting: file moved to %@", newReadingURL);
exit(1);
}
// Launch our UNIX command
NSString *launchPath = [toolArguments objectAtIndex:0];
[toolArguments removeObjectAtIndex:0];
NSLog(@"Launching %@...", launchPath);
NSTask *task = [NSTask launchedTaskWithLaunchPath:launchPath arguments:toolArguments];
[task waitUntilExit];
taskTerminationStatus = [task terminationStatus];
if (taskTerminationStatus == 0) {
NSLog(@" ... done");
} else {
NSLog(@" ... exited with status %d", taskTerminationStatus);
}
}];
if (fileCoordinatorError != nil) {
NSLog(@"File coordination error: %@: %@", [fileCoordinatorError localizedDescription], [fileCoordinatorError localizedFailureReason]);
exit(1);
}
}
return taskTerminationStatus;
}
Here’s an example of how I might use this tool to edit an open OmniOutliner document using some UNIX commands:
./file-coord /tmp/test-coord.oo3 -- /bin/zsh -c 'sed "s/Hello.*</Hello, world</" < /tmp/test-coord.oo3/contents.xml > /tmp/new-contents.xml; mv /tmp/new-contents.xml /tmp/test-coord.oo3/contents.xml'
Of course, you can also use it to edit other things, like open TextEdit documents. For example, this command will grab the current contents of an open TextEdit /tmp/foo.txt window (saving its contents if the user has unsaved edits) and replace them with “hello world”:
./file-coord /tmp/foo.txt -- /bin/zsh -c 'cat /tmp/foo.txt; echo hello world > /tmp/foo.txt'
Hope this helps!