0

I found something with NSTask to execute a command but I can't get it work.

This is my command:

ioreg -c BNBMouseDevice | grep BatteryPercent

How can I run THIS in my cocoa app? (programmatically of course)

Thank you very much for your help.

I have tried this:

NSTask *server = [NSTask new];
[server setLaunchPath:@"/bin/sh"];
[server setArguments:[NSArray arrayWithObject:@"ioreg -c BNBMouseDevice | grep BatteryPercent"]];
NSPipe *outputPipe = [NSPipe pipe];
[server setStandardInput:[NSPipe pipe]];
[server setStandardOutput:outputPipe];
[server launch];
[server waitUntilExit];
[server release];
NSData *outputData = [[outputPipe fileHandleForReading] readDataToEndOfFile];
NSString *outputString = [[[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding] autorelease]; 

but it says no such file or directory but if i do this in terminal:

ioreg -c BNBMouseDevice | grep BatteryPercent

it works!

what's wrong?

Abizern
  • 146,289
  • 39
  • 203
  • 257
cocos2dbeginner
  • 2,185
  • 1
  • 31
  • 58
  • There are quite a few examples here on SO already of using `NSTask` and `NSPipe` to do this kind of thing, e.g. http://stackoverflow.com/questions/3444178/nstask-nspipe-objective-c-command-line-help – Paul R Jan 07 '11 at 17:04
  • Hi tried this:NSTask *server = [NSTask new]; [server setLaunchPath:@"/bin/sh"]; [server setArguments:[NSArray arrayWithObject:@"ioreg -c BNBMouseDevice | grep BatteryPercent"]]; NSPipe *outputPipe = [NSPipe pipe]; [server setStandardInput:[NSPipe pipe]]; [server setStandardOutput:outputPipe]; [server launch]; [server waitUntilExit]; [server release]; NSData *outputData = [[outputPipe fileHandleForReading] readDataToEndOfFile]; NSString *outputString = [[[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding] autorelease]; – cocos2dbeginner Jan 07 '11 at 17:15
  • but it says no such file or directory but if i do this in terminal:ioreg -c BNBMouseDevice | grep BatteryPercent //it works!what's wrong? – cocos2dbeginner Jan 07 '11 at 17:15

2 Answers2

3

NSTask launches a single command that takes a set of arguments as an array of strings. NSTask does not do any of the shell parsing magic, nor can you pass the string to sh as an argument and have it parse it in that fashion. There is probably a way to do so, but there is a better solution.

Simply run the ioreg command via NSTask, grab the output via the standard means, then do a simple enumeration of the returned string to find the line(s) you want. While a few more lines of code, it'll be considerably less fragile (and less of a security hole if you ever supply arguments of some kind) than trying to pump it through the shell. And it is easy to do.

See this document for more information.

bbum
  • 162,346
  • 23
  • 271
  • 359
2

When invoking /bin/sh, you need to pass -c if you want the shell to run a command. It should be equivalent to:

/bin/sh -c "/usr/sbin/ioreg -c BNBMouseDevice | /usr/bin/grep BatteryPercent"

You can do this by replacing

[server setArguments:[NSArray arrayWithObject:@"ioreg -c BNBMouseDevice | grep BatteryPercent"]];

with

[server setArguments:[NSArray arrayWithObjects:@"-c", @"/usr/sbin/ioreg -c BNBMouseDevice | /usr/bin/grep BatteryPercent", nil]];

Notice that I’ve made two changes: the arguments are an array with two elements, namely "-c" and "/usr/sbin/ioreg -c BNBMouseDevice | /usr/bin/grep BatteryPercent", and I’ve also replaced ioreg with /usr/sbin/ioreg (and grep with /usr/bin/grep) to make it explicit that you’re interested in those particular programs.

  • i get this as output: http://pastie.org/1437941 should i make an nsdict and then make this: valueForKey: BatteryPercent" ? Please help me. (i already marked your answer as right ;) – cocos2dbeginner Jan 07 '11 at 18:42
  • so i tried to make a dict but this doesn't work. But i only want "BatteryPercent". How can i get this? (first say me that and i will tick it as a right answer);) – cocos2dbeginner Jan 07 '11 at 18:49
  • As far as I know, you cannot convert that output directly to an NSDictionary (maybe with NeXT-style property list format, and you’d need to trim the | characters anyway). One possible solution is to change your grep invocation to `grep "\"BatteryPercent\" = [0-9]"` so that it’ll only match `"BatteryPercent" = ` followed by a digit, and pipe it to `cut`, e.g. `grep "\"BatteryPercent\" = [0-9]" c.txt | cut -d= -f2` (with proper escaping). However, that seems rather fragile and IMO should be replaced by using an API (IOKit?) instead of running `ioreg`. –  Jan 07 '11 at 18:55
  • 1
    Also, having accepted my answer, reverting the acceptance, and then blackmailing the acceptance for more information because you’re having further problems than those listed in the question… that’s bad form. –  Jan 07 '11 at 19:00