Getting data from the nstask - communicating with command line - objective c
Asked Answered
M

2

15

I know how to send data to the task:

NSData *charlieSendData = [[charlieImputText stringValue] dataUsingEncoding:NSUTF8StringEncoding];
[[[task standardInput] fileHandleForWriting] writeData:charlieSendData];

But how do I get what the task responds with??

Elijah

Malathion answered 9/8, 2010 at 19:15 Comment(0)
A
35

Give an NSPipe or an NSFileHandle as the task's standardOutput, and read from that.

NSTask * list = [[NSTask alloc] init];
[list setLaunchPath:@"/bin/ls"];
[list setCurrentDirectoryPath:@"/"];

NSPipe * out = [NSPipe pipe];
[list setStandardOutput:out];

[list launch];
[list waitUntilExit];
[list release];

NSFileHandle * read = [out fileHandleForReading];
NSData * dataRead = [read readDataToEndOfFile];
NSString * stringRead = [[[NSString alloc] initWithData:dataRead encoding:NSUTF8StringEncoding] autorelease];
NSLog(@"output: %@", stringRead);

Note that if you use a pipe, you have to worry about the pipe filling up. If you provide an NSFileHandle instead, the task can output all it wants without you having to worry about losing any, but you also get the overhead of having to write the data out to disk.

Ashla answered 9/8, 2010 at 19:19 Comment(4)
I already do that. [task standardOutput] - If I just call this, will it give the output?Malathion
@Elijah by default, no. If you want the output, you have to provide a pipe or filehandle before launching the task, and then start reading from the filehandle (or [pipe fileHandleForReading]) in order to get the data back. (And it will give you NSData objects, not strings or anything)Ashla
How can it be modified to get data ASAP not waiting until process finished?Franconia
@4ntoine, check out this SO answer. You can call waitForDataInBackgroundAndNotify on the NSFileHandle returned from NSPipe's fileHandleForReading. You can then listen for the NSFileHandleDataAvailableNotification with NSNotificationCenter.Fred
C
4

Swift 3 solution, you can implement a closure that accepts a FileHandle

let process = Process()
process.launchPath = launchPath
process.arguments = arguments

let stdOut = Pipe()
process.standardOutput = stdOut
let stdErr = Pipe()
process.standardError = stdErr

let handler =  { (file: FileHandle!) -> Void in
  let data = file.availableData
  guard let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
    else { return}

  print(output.components(separatedBy: "\n").first!)
}

stdErr.fileHandleForReading.readabilityHandler = handler
stdOut.fileHandleForReading.readabilityHandler = handler

process.terminationHandler = { (task: Process?) -> () in
  stdErr.fileHandleForReading.readabilityHandler = nil
  stdOut.fileHandleForReading.readabilityHandler = nil
}

process.launch()
process.waitUntilExit()
Cession answered 11/3, 2017 at 17:38 Comment(2)
Notably, stdout/stdin are buffered and if your command outputs enough data, the pipe will hang, so this solution using closures is the one you should use.Edina
@Edina by "enough" you mean "too much", I assume? You mean that if the tool could output more data than fits into the memory buffers, and then it'll block until some process empties the buffers?Cristie

© 2022 - 2024 — McMap. All rights reserved.