2011-08-03 22 views
18

Tengo un script PHP que tiene comandos mutliple sleep(). Me gustaría ejecutarlo en mi aplicación con NSTask. Mi script es el siguiente:Salida en tiempo real de NSTask

echo "first\n"; sleep(1); echo "second\n"; sleep(1); echo "third\n"; 

puedo ejecutar mi tarea de forma asíncrona mediante notificaciones:

- (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]; 

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskExited:) name:NSTaskDidTerminateNotification object:task]; 

    [task launch]; 

} 

- (void)taskExited:(NSNotification *)notif { 
    NSTask *task = [notif object]; 
    NSData *data = [[[task standardOutput] fileHandleForReading] readDataToEndOfFile]; 
    NSString *str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; 
    NSLog(@"%@",str); 
} 

Mi salida es (después de 2 segundos, por supuesto):

2011-08-03 20:45:19.474 MyApp[3737:903] first 
second 
third 

Mi pregunta es: ¿cómo puedo obtener estas tres palabras inmediatamente después de que se impriman?

Respuesta

20

Puede utilizar el método waitForDataInBackgroundAndNotify de NSFileHandle para recibir una notificación cuando el script escribe datos en su salida. Sin embargo, esto solo funcionará si el intérprete envía las cadenas de inmediato. Si almacena la salida, recibirá una notificación única después de que la tarea finalice.

- (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); 
    } 
} 
+0

Lo intenté, pero solo se registra "primero". "segundo" y "tercero" se registran en la terminación de la tarea ... – akashivskyy

+0

Se resolvió colocando 'readInBackgroundAndNotify' extra en' -receiveData'. ¡Gracias por la idea! – akashivskyy

+4

Actualicé el código después de luchar por un tiempo. Necesitabas otro 'waitForDataInBackgroundAndNotify', no' readInBackgroundAndNotify'. – citelao

9

Como referencia, aquí va la respuesta de ughoavgfhw 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!) 
    } 
} 

Esta construcción será necesaria si su tarea produce una gran cantidad de salida en la tubería. Simplemente llamando al pipe.fileHandleForReading.readDataToEndOfFile() no funcionará porque la tarea está esperando a que la tubería se vacíe para que pueda escribir más mientras su programa está esperando el final de los datos. Por lo tanto, su programa se bloqueará. Esta construcción de notificación y observación permite que la tubería se lea de forma asíncrona y, por lo tanto, evita el estancamiento antes mencionado.