How to run NSTask with multiple commands - objective-c

I'm trying to make a NSTask running a command like this:
ps -clx | grep 'Finder' | awk '{print $2}'
Here is my method
- (void) processByName:(NSString*)name {
NSTask *task1 = [[NSTask alloc] init];
NSPipe *pipe1 = [NSPipe pipe];
[task1 waitUntilExit];
[task1 setLaunchPath: #"/bin/ps"];
[task1 setArguments: [NSArray arrayWithObjects: #"-clx", nil]];
[task1 setStandardOutput: pipe1];
NSTask *task2 = [[NSTask alloc] init];
NSPipe *pipe2 = [NSPipe pipe];
[task2 setLaunchPath: #"/usr/bin/grep"];
[task2 setArguments: [NSArray arrayWithObjects: #"'Finder'", nil]];
[task2 setStandardInput:pipe1];
[task2 setStandardOutput: pipe2];
NSTask *task3 = [[NSTask alloc] init];
NSPipe *pipe3 = [NSPipe pipe];
[task3 setLaunchPath: #"/usr/bin/grep"];
[task3 setArguments: [NSArray arrayWithObjects: #"'{print $2}'", nil]];
[task3 setStandardInput:pipe2];
[task3 setStandardOutput: pipe3];
NSFileHandle *file = [pipe3 fileHandleForReading];
[task1 launch];
[task2 launch];
[task3 launch];
NSData *data;
data = [file readDataToEndOfFile];
NSString *string;
string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(#"Result: %#", string);
}
But the result is just
Result:
What am I doing wrong?
EDIT
- (void) processByName:(NSString*)name {
NSTask *task1 = [[NSTask alloc] init];
NSPipe *pipe1 = [NSPipe pipe];
[task1 waitUntilExit];
[task1 setLaunchPath: #"/bin/ps"];
[task1 setArguments: [NSArray arrayWithObjects: #"-clx", nil]];
[task1 setStandardOutput: pipe1];
NSTask *task2 = [[NSTask alloc] init];
NSPipe *pipe2 = [NSPipe pipe];
[task2 setLaunchPath: #"/usr/bin/grep"];
[task2 setArguments: [NSArray arrayWithObjects: #"'Finder'", nil]];
[task2 setStandardInput:pipe1];
[task2 setStandardOutput: pipe2];
NSTask *task3 = [[NSTask alloc] init];
NSPipe *pipe3 = [NSPipe pipe];
[task3 setLaunchPath: #"/usr/bin/grep"];
[task3 setArguments: [NSArray arrayWithObjects: #"'{print $2}'", nil]];
[task3 setStandardInput:pipe2];
[task3 setStandardOutput: pipe3];
NSFileHandle *file = [pipe3 fileHandleForReading];
[task1 launch];
[task2 launch];
[task3 launch];
[[NSNotificationCenter defaultCenter] addObserverForName:NSTaskDidTerminateNotification
object:task3
queue:nil
usingBlock:^(NSNotification* notification){
NSData * data = [file readDataToEndOfFile];
NSString * string;
string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(#"Result: %#", string);
}];
}

The tasks run in a separate process from your code, i.e., asychronously. They probably haven't finished (they may not have even launched!) by the time you get to the readDataToEndOfFile two lines later.
If you're already on a background thread here, you can poll their status: while( ![task isRunning]){, or if you're on the main thread, I'd suggest using GCD to put this onto a queue and doing the polling there.
Actually, better than that would be to use notifications:
[task3 launch];
[[NSNotificationCenter defaultCenter] addObserverForName:NSTaskDidTerminateNotification
object:task3
queue:nil
usingBlock:^{
NSData * data = [file readDataToEndOfFile];
NSString * string;
string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(#"Result: %#", string);
}];
See TN2050: Observing Process Lifetime Without Polling. Each NSTask will send NSTaskDidTerminateNotification when it terminates (you should, ideally, check its return code rather than assuming it ran successfully). You can create a block to be run when task3 sends that notification.

The following code works for me.
NSTask *task1 = [[NSTask alloc] init];
NSPipe *pipe1 = [NSPipe pipe];
[task1 waitUntilExit];
[task1 setLaunchPath: #"/bin/sh"];
[task1 setArguments: [NSArray arrayWithObjects: #"-c",#"ps -A |grep -m1 Finder | awk '{print $1}'", nil]];
[task1 setStandardOutput: pipe1];
[task1 launch];
NSFileHandle *file = [pipe1 fileHandleForReading];
NSData * data = [file readDataToEndOfFile];
NSString * string;
string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(#"Result: %#", string);

Almost 8 years later, I think grep binary is'nt in /usr/bin, for me it's in /bin. Also, for your awk command, you set the launch path to grep.

Related

See NSTask output Cocoa

How could I make an if statement that's like:
if(the output of a nstask is equal to a specific string){
//do stuff over here
}
I'm running a NSTask and it put's out the data that's coming from it in the NSLog, but how could I not show it there but store it as a NSString or something like that
This is what my task looks like
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/usr/bin/csrutil"];
[task setArguments:#[ #"status" ]];
[task launch];
Any help is greatly appreciated :)
You need a pipe and a file handle to read the result.
Write a method
- (NSString *)runShellScript:(NSString *)cmd withArguments:(NSArray *)args
{
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:cmd];
[task setArguments:args];
NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file = [pipe fileHandleForReading];
[task launch];
NSData *data = [file readDataToEndOfFile];
return [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
}
and call it
NSString *result = [self runShellScript:#"/usr/bin/csrutil" withArguments:#[ #"status" ]];

How to run sudo command programmatically on OS X

I am trying to run sudo command programmatically in my OS X app,
but i got these message
sudo: no tty present and no askpass program specified
Here is my code:
NSString *runCommand(NSString *commandToRun)
{
NSTask *task;
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;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file;
file = [pipe fileHandleForReading];
[task launch];
NSData *data;
data = [file readDataToEndOfFile];
NSString *output;
output = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
return output;
}
And i call this method like this :
runCommand(#"sudo purge");
Thanks for any help or sugession.

NSTask and FFMpeg losing output

I'm trying to call ffmpeg from NSTask in objective-c. I execute the ffmpeg command in terminal and it works flawlessly every time. I make the same command using NSTask, and it never gives me the whole output. It cuts it off half way through the output, at a seemingly random spot every time. Here is my code.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSString* ffmpegPath = [[NSBundle mainBundle] pathForResource:#"ffmpeg" ofType:#""];
NSString* path = #"test.mov";
NSTask *task = [[NSTask alloc] init];
NSArray *arguments = [NSArray arrayWithObjects: #"-i", path, nil];
NSPipe *pipe = [NSPipe pipe];
NSFileHandle * read = [pipe fileHandleForReading];
[task setLaunchPath: ffmpegPath];
[task setArguments: arguments];
[task setStandardOutput: pipe];
[task launch];
[task waitUntilExit];
NSData* data = [read readDataToEndOfFile];
NSString* stringOutput = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", stringOutput);
NSLog(#"%i", [task terminationStatus]);
NSLog(#"DONE");
}
And just like that I figured it out. Apparently the output had non UTF8Characters in it. Switched it over to NSASCIIStringEncoding and voila. Magic.

executing shell command with | (pipe) using NSTask

I'm trying to execute this comamnd ps -ef | grep test using NSTask but I can't get the | grep test to be included in the NSTask:
This is what I'm using currently to get the output of ps -ef into a string then I need to somehow get the pid of the process test
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/ps"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects: #"-ef", 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 (#"got\n%#", string);
Piping is a feature provided by shells, such as /bin/sh. You may try launching your command via such a shell:
/* ... */
[task setLaunchPath: #"/bin/sh"];
/* ... */
arguments = [NSArray arrayWithObjects: #"-c", #"ps -ef | grep test", nil];
However, if you let the user supply a value (instead of hard-coding e.g. test), you are making the program susceptible to shell injection attacks, which are kind of like SQL injection. An alternative, which doesn't suffer from this problem, is to use a pipe object to connect the standard output of ps with the standard input of grep:
NSTask *psTask = [[NSTask alloc] init];
NSTask *grepTask = [[NSTask alloc] init];
[psTask setLaunchPath: #"/bin/ps"];
[grepTask setLaunchPath: #"/bin/grep"];
[psTask setArguments: [NSArray arrayWithObjects: #"-ef", nil]];
[grepTask setArguments: [NSArray arrayWithObjects: #"test", nil]];
/* ps ==> grep */
NSPipe *pipeBetween = [NSPipe pipe];
[psTask setStandardOutput: pipeBetween];
[grepTask setStandardInput: pipeBetween];
/* grep ==> me */
NSPipe *pipeToMe = [NSPipe pipe];
[grepTask setStandardOutput: pipeToMe];
NSFileHandle *grepOutput = [pipeToMe fileHandleForReading];
[psTask launch];
[grepTask launch];
NSData *data = [grepOutput readDataToEndOfFile];
/* etc. */
This uses built-in Foundation functionality to perform the same steps as the shell does when it encounters the | character.
Finally as others have pointed out, the usage of grep is overkill. Just add this to your code:
NSArray *lines = [string componentsSeparatedByString:#"\n"];
NSArray *filteredLines = [lines filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: #"SELF contains[c] 'test'"]];
You may need to call [task waitUntilExit] before you launch the task, so that the process can finish running before you read the output.

Convert NSTask "/bin/sh -c" command into proper pipeline code

Can someone help me convert the following code into code that instead has two NSTasks for "cat" and "grep", showing how the two can be connected together with pipes? I suppose I would prefer the latter approach, since then I no longer have to worry about quoting and stuff.
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/sh"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects: #"-c",
#"cat /usr/share/dict/words | grep -i ham", nil];
[task setArguments: arguments];
[task launch];
Update: Note that cat and grep are here just meant as (lousy) example. I still want to do this for commands that make more sense.
Use a instance of NSTask for each program and connect their standard inputs/outputs with NSPipe:
NSPipe *pipe = [[NSPipe alloc] init];
NSPipe *resultPipe = [[NSPipe alloc] init];
NSTask *task1 = [[NSTask alloc] init];
[task1 setLaunchPath: #"/bin/cat"];
[task1 setStandardOutput: pipe];
[task1 launch];
NSTask *task2 = [[NSTask alloc] init];
[task2 setLaunchPath: #"/bin/grep"];
[task2 setStandardInput: pipe];
[task2 setStandardOutput: resultPipe];
[task2 launch];
NSData *result = [[resultPipe fileHandleForReading] readDataToEndOfFile];