How do I get something similar to Tail -f using NSTask
I need to read the last added line to a log file, in realtime, and capture that line being added.
Something similar to Tail -f.
So my first attempt was to use Tail -f using NSTask.
I can't see any output using the code below:
NSTask *server = [[NSTask alloc] init];
[server setLaunchPath:@"/usr/bin/tail"];
[server setArguments:[NSArray arrayWithObjects:@"-f", @"/path/to/my/LogFile.txt",nil]];
NSPipe *outputPipe = [NSPipe pipe];
[server setStandardInput:[NSPipe pipe]];
[server setStanda开发者_JAVA百科rdOutput:outputPipe];
[server launch];
[server waitUntilExit];
[server release];
NSData *outputData = [[outputPipe fileHandleForReading] readDataToEndOfFile];
NSString *outputString = [[[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding] autorelease];
NSLog (@"Output \n%@", outputString);
I can see the output as expected when using:
[server setLaunchPath:@"/bin/ls"];
How can i capture the output of that tail NSTask?
Is there any alternative to this method, where I can open a stream to file and each time a line is added, output it on screen? (basic logging functionality)
This is a little tricky to do your way, as readDataToEndOfFile
will wait until tail
closes the output stream before returning, but tail -f
never closes the output stream (stdout). However, this is actually pretty simple to do with basic C I/O code, so I whipped up a simple FileTailer
class that you can check out. It's not anything fancy, but it should show you how it's done. Here're the sources for FileTailer.h
, FileTailer.m
, and a test driver.
The meat of the class is pretty simple. You pass it a block, and it reads a character from the stream (if possible) and passes it to the block; if EOF has been reached, it waits a number of seconds (determined by refresh
) and then tries to read the stream again.
- (void)readIndefinitely:(void (^)(int ch))action
{
long pos = 0L;
int ch = 0;
while (1) {
fseek(in, pos, SEEK_SET);
int ch = fgetc(in);
pos = ftell(in);
if (ch != EOF) {
action(ch);
} else {
[NSThread sleepForTimeInterval:refresh];
}
}
}
You can call it pretty simply, like this:
FileTailer *tail = [[[FileTailer alloc] initWithStream:stdin refreshPeriod:3.0] autorelease];
[tail readIndefinitely:^ void (int ch) { printf("%c", ch); }];
(Caveat: I wrote the FileTailer
class pretty fast, so it's kind of ugly right now and should be cleaned up a bit, but it should serve as a decent example on how to read a file indefinitely, à la tail -f
.)
Here's a way to use "tail -f logfile" via NSTask in Objective-C:
asynctask.m -- sample code that shows how to implement asynchronous stdin, stdout & stderr streams for processing data with NSTask
...
Being a GUI-less application (i.e. a Foundation-based command line tool), asynctask.m runs an NSRunLoop manually to enable the use of asynchronous "waitForDataInBackgroundAndNotify" notifications. In addition, asynctask.m uses pthread_create(3) and pthread_detach(3) for writing more than 64 KB to the stdin of an NSTask.
Source code of asynctask.m available at: http://www.cocoadev.com/index.pl?NSPipe
精彩评论