4

I have something like:

- (NSString *)unixSinglePathCommandWithReturn:(NSString *)command
{
NSPipe *newPipe = [NSPipe pipe];
NSFileHandle *readHandle = [newPipe fileHandleForReading];

NSTask *unixTask = [[NSTask alloc] init];
[unixTask setStandardOutput:newPipe];
[unixTask setLaunchPath:@"/bin/sh"];
[unixTask setArguments:[NSArray arrayWithObjects:@"-c", command , nil]]; 
[unixTask launch];
[unixTask waitUntilExit];

NSString *output = [[NSString alloc] initWithData:[readHandle readDataToEndOfFile] encoding:NSUTF8StringEncoding];

return output;
}

But it doesn't work as expected. I call it when i click a button. If i remove the line with 'waitUntilExit' it works as expected, but only once. When it's there, it doesn't work. I tried also some basic commands like 'ls' 'ping -c1 google.com' and stuff like that, but i can't get it to work somehow. If you have some different approach to run shell scripts in cocoa with receiving the response, please let me now. Thank you all :)

Kukosk
  • 2,892
  • 1
  • 27
  • 30
  • Hi, interesting - this code works well for me. However for some reason it messes with NSLog - and NSLog won't display anything for me, but when run directly from the console it will work as expected. I haven't changed your code in any way .... – Moszi Jan 10 '11 at 22:47
  • possible duplicate of [Execute a terminal command from a Cocoa app](http://stackoverflow.com/questions/412562/execute-a-terminal-command-from-a-cocoa-app) – mmmmmm Sep 14 '14 at 21:31

4 Answers4

5

Hey, Kukosk. There’s a comment on CocoaDev about NSLog() issues when running an NSTask. The fix is to set a pipe for stdin before launching the task:

[task setStandardInput:[NSPipe pipe]];

If you’re relying on NSLog() only to check whether the task has run, this might fix your problem. Alternatively, you could try to present output in your GUI instead of using NSLog().

  • thanks @bavarious you saved my day! Wait, aren't you the same person in another NSTask thread??? :) God bless you... – swdev Aug 23 '11 at 15:36
2

The problem is that you aren't emptying the output buffers of the task. You can't simply launch a task and waitUntilDone unless the task also emits an extremely small amount of data.

waitUntilDone will obviously not work at all with a task that never exits.

For a task that emits any quantity of output, you need to set it up such that the output is read as it is generated. Typically, you use readInBackgroundAndNotify or a variant therein.

In any case, the top of the class description for NSTask has both links to the conceptual guide and to a series of examples that cover this.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • So what do i have to do in general? Can u please post the working code with a bit explanation, please? It will really help! I am familiar with obj.c, but i was coding for the iOS platform, so some things i have never used are kinda new for me :) thank you for your response. – Kukosk Jan 11 '11 at 01:54
  • 1
    The NSTask documentation goes in depth on the subject, including extensive examples. – bbum Jan 11 '11 at 06:45
0

Ah, there's a very important line in the docs you seem to have missed, one of those irritations that NextStep seems to like: "An NSTask object can only be run once. Subsequent attempts to run the task raise an error."

So, bag the wait, and add [unixTask release] before the return. When you want to run it again, remake the task.

NSTimer is like this.

Hack Saw
  • 2,741
  • 1
  • 18
  • 33
  • OP is making a new task on each invocation to that method. That isn't the problem (though you are correct in that said code is leaking the task). – bbum Jan 10 '11 at 23:57
  • He’d also be leaking the output string, except that I know for a fact he’s using garbage collection. Hey, Kukosk. =P –  Jan 11 '11 at 01:57
  • im using the gc ;) but i will release it since it'll work even if i use gc – Kukosk Jan 11 '11 at 02:11
0

For different approaches to run shell scripts in Cocoa have a look at AMShellWrapper, PseudoTTY.app or OpenFileKiller!

http://www.cocoadev.com/index.pl?NSTask

tim
  • 1