2011-08-03 33 views
18

sleep() numaralı komutları içeren bir PHP betiğim var. Uygulamamda NSTask ile yürütmek istiyorum. Benim komut dosyası aşağıdaki gibi görünür: (tabii 2 saniye sonra)NSTask'nın gerçek zamanlı çıktısı

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

Benim çıkışı: Ben bildirimleri kullanarak uyumsuz görevimi yürütebileceği

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

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

Sorum şu: Bu üç kelimeyi basıldıktan hemen sonra nasıl alabilirim?

cevap

20

Komut dosyası verileri çıktıya yazdığında bildirim almak için NSFileHandle'in waitForDataInBackgroundAndNotify yöntemini kullanabilirsiniz. Ancak bu sadece tercüman dizeleri derhal gönderirse çalışacaktır. Çıktıyı arabelleğe alırsa, görev çıktıktan sonra tek bir bildirim alırsınız.

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

Bunu denedim, ancak yalnızca "ilk" olarak günlüğe kaydeder. Görev sonlandırıldığında "ikinci" ve "üçüncü" kaydediliyor ... – akashivskyy

+0

"-receiveData" içine fazla 'readInBackgroundAndNotify 'yerleştirerek çözüldü. Fikir için teşekkürler! – akashivskyy

+4

Bir süre uğraştıktan sonra kodu güncelledim. Başka bir 'waitForDataInBackgroundAndNotify ',' readInBackgroundAndNotify' değil. – citelao

9

Referans için, burada hızlı bir şekilde ughoavgfhw yanıtı.

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!) 
    } 
} 

Göreviniz, boruya çok miktarda çıktı üretiyorsa bu yapı gerekli olacaktır. Basit bir şekilde pipe.fileHandleForReading.readDataToEndOfFile() numaralı çağrı işe yaramayacaktır çünkü görev borunun boşaltılmasını beklemektedir, böylece programınız veri bitimini beklerken daha fazla yazabilir. Böylece programınız asılacaktır. Bu bildirim ve gözlemci yapısı, borunun asenkron olarak okunmasına izin verir ve dolayısıyla yukarıda belirtilen çıkmazı engeller.

İlgili konular