Execute a terminal command from a Cocoa app
You can use NSTask
. Here's an example that would run '/usr/bin/grep foo bar.txt
'.
int pid = [[NSProcessInfo processInfo] processIdentifier];NSPipe *pipe = [NSPipe pipe];NSFileHandle *file = pipe.fileHandleForReading;NSTask *task = [[NSTask alloc] init];task.launchPath = @"/usr/bin/grep";task.arguments = @[@"foo", @"bar.txt"];task.standardOutput = pipe;[task launch];NSData *data = [file readDataToEndOfFile];[file closeFile];NSString *grepOutput = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];NSLog (@"grep returned:\n%@", grepOutput);
NSPipe
and NSFileHandle
are used to redirect the standard output of the task.
For more detailed information on interacting with the operating system from within your Objective-C application, you can see this document on Apple's Development Center: Interacting with the Operating System.
Edit: Included fix for NSLog problem
If you are using NSTask to run a command-line utility via bash, then you need to include this magic line to keep NSLog working:
//The magic line that keeps your log where it belongstask.standardOutput = pipe;
An explanation is here: https://web.archive.org/web/20141121094204/https://cocoadev.com/HowToPipeCommandsWithNSTask
kent's article gave me a new idea. this runCommand method doesn't need a script file, just runs a command by a line:
- (NSString *)runCommand:(NSString *)commandToRun{ NSTask *task = [[NSTask alloc] init]; [task setLaunchPath:@"/bin/sh"]; NSArray *arguments = [NSArray arrayWithObjects: @"-c" , [NSString stringWithFormat:@"%@", commandToRun], nil]; NSLog(@"run command:%@", commandToRun); [task setArguments:arguments]; NSPipe *pipe = [NSPipe pipe]; [task setStandardOutput:pipe]; NSFileHandle *file = [pipe fileHandleForReading]; [task launch]; NSData *data = [file readDataToEndOfFile]; NSString *output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; return output;}
You can use this method like this:
NSString *output = runCommand(@"ps -A | grep mysql");
in the spirit of sharing... this is a method I use frequently to run shell scripts.you can add a script to your product bundle (in the copy phase of the build) and thenhave the script be read and run at runtime. note: this code looks for the script in the privateFrameworks sub-path.warning: this could be a security risk for deployed products, but for our in-house development it is an easy way to customize simple things (like which host to rsync to...) without re-compiling the application, but just editing the shell script in the bundle.
//-------------------------------------------------------(void) runScript:(NSString*)scriptName{ NSTask *task; task = [[NSTask alloc] init]; [task setLaunchPath: @"/bin/sh"]; NSArray *arguments; NSString* newpath = [NSString stringWithFormat:@"%@/%@",[[NSBundle mainBundle] privateFrameworksPath], scriptName]; NSLog(@"shell script path: %@",newpath); arguments = [NSArray arrayWithObjects:newpath, nil]; [task setArguments: arguments]; NSPipe *pipe; pipe = [NSPipe pipe]; [task setStandardOutput: pipe]; NSFileHandle *file; file = [pipe fileHandleForReading]; [task launch]; NSData *data; data = [file readDataToEndOfFile]; NSString *string; string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]; NSLog (@"script returned:\n%@", string); }//------------------------------------------------------
Edit: Included fix for NSLog problem
If you are using NSTask to run a command-line utility via bash, then you need to include this magic line to keep NSLog working:
//The magic line that keeps your log where it belongs[task setStandardInput:[NSPipe pipe]];
In context:
NSPipe *pipe;pipe = [NSPipe pipe];[task setStandardOutput: pipe];//The magic line that keeps your log where it belongs[task setStandardInput:[NSPipe pipe]];
An explanation is here: http://www.cocoadev.com/index.pl?NSTask