NSTask's real-time output

烈酒焚心 提交于 2019-11-30 01:48:50
ughoavgfhw

You can use NSFileHandle's waitForDataInBackgroundAndNotify method to receive a notification when the script writes data to its output. This will only work, however, if the interpreter sends the strings immediately. If it buffers output, you will get a single notification after the task exits.

- (void)awakeFromNib {
    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath: @"/usr/bin/php"];

    NSArray *arguments;
    arguments = [NSArray arrayWithObjects: @"-r", @"echo \"first\n\"; sleep(1); echo \"second\n\"; sleep(1); echo \"third\n\";", nil];
    [task setArguments: arguments];

    NSPipe *p = [NSPipe pipe];
    [task setStandardOutput:p];
    NSFileHandle *fh = [p fileHandleForReading];
    [fh waitForDataInBackgroundAndNotify];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedData:) name:NSFileHandleDataAvailableNotification object:fh];

    [task launch];

}

- (void)receivedData:(NSNotification *)notif {
    NSFileHandle *fh = [notif object];
    NSData *data = [fh availableData];
    if (data.length > 0) { // if data is found, re-register for more data (and print)
        [fh waitForDataInBackgroundAndNotify];
        NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%@" ,str);
    }
}

For reference, here is ughoavgfhw's answer in swift.

override func awakeFromNib() {
    // Setup the task
    let task = NSTask()
    task.launchPath = "/usr/bin/php"
    task.arguments = ["-r", "echo \"first\n\"; sleep(1); echo \"second\n\"; sleep(1); echo \"third\n\";"]

    // Pipe the standard out to an NSPipe, and set it to notify us when it gets data
    let pipe = NSPipe()
    task.standardOutput = pipe
    let fh = pipe.fileHandleForReading
    fh.waitForDataInBackgroundAndNotify()

    // Set up the observer function
    let notificationCenter = NSNotificationCenter.defaultCenter()
    notificationCenter.addObserver(self, selector: "receivedData:", name: NSFileHandleDataAvailableNotification, object: nil)

    // You can also set a function to fire after the task terminates
    task.terminationHandler = {task -> Void in
           // Handle the task ending here
    }

    task.launch()
}

func receivedData(notif : NSNotification) {
    // Unpack the FileHandle from the notification
    let fh:NSFileHandle = notif.object as NSFileHandle
    // Get the data from the FileHandle
    let data = fh.availableData
    // Only deal with the data if it actually exists
    if data.length > 1 {
    // Since we just got the notification from fh, we must tell it to notify us again when it gets more data
        fh.waitForDataInBackgroundAndNotify()
        // Convert the data into a string
        let string = NSString(data: data, encoding: NSASCIIStringEncoding)
        println(string!)
    }
}

This construct will be necessary if your task produces lots of output into the pipe. Simply calling pipe.fileHandleForReading.readDataToEndOfFile() will not work because the task is waiting for the pipe to be emptied so it can write more while your program is waiting for the end of the data. Thus, your program will hang. This notification and observer construct allows the pipe to be read asynchronously and thus prevents the aforementioned stalemate.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!