macos - Salida en tiempo real de NSTask
(2)
Tengo una secuencia de comandos PHP que tiene comandos mutliple sleep()
. Me gustaría ejecutarlo en mi aplicación con NSTask
. Mi guión se ve así:
echo "first/n"; sleep(1); echo "second/n"; sleep(1); echo "third/n";
Puedo ejecutar mi tarea de forma asincrónica usando 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 imprimen?
Para referencia, aquí está la respuesta de ughoavgfhw en 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 mucha salida en la tubería. Simplemente llamando a pipe.fileHandleForReading.readDataToEndOfFile()
no funcionará porque la tarea está esperando a que se vacíe la tubería para que pueda escribir más mientras el programa está esperando el final de los datos. Por lo tanto, su programa se cuelga. 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 punto muerto mencionado anteriormente.
Puede usar el método waitForDataInBackgroundAndNotify de waitForDataInBackgroundAndNotify
para recibir una notificación cuando el script escriba datos en su salida. Sin embargo, esto solo funcionará si el intérprete envía las cadenas inmediatamente. Si amortigua la salida, recibirá una única notificación después de que finalice la tarea.
- (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);
}
}