NSTask - execute echo command - objective-c

I'm trying to run a simple task which has to execute an echo "Hello World"
Well here is my code:
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath:#"/bin/bash"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects:#"echo","hello world" nil];
[task setArguments: arguments];
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
[task setStandardError: pipe];
NSFileHandle *file;
file = [pipe fileHandleForReading];
[task launch];
//...
//Code to get task response
Keep giving me no such file or directory error.. What am I doing wrong ?

The right way to execute a command is
bash -c "echo 'hello world'"
which means the arguments you should pass are
arguments = [NSArray arrayWithObjects:#"-c", #"echo 'hello world'", nil];

Related

Launching command using NSTask returns error

I'd like to launch the following command from my application using NSTask:
sudo -u myusername launchctl load /Library/LaunchAgents/com.google.keystone.agent.plist
Here is a code I do:
NSPipe *pipe = [NSPipe pipe];
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/sh"];
[task setCurrentDirectoryPath:#"/"];
[task setStandardError:pipe];
NSArray *arguments = nil;
arguments = #[#"sudo",
#"-u",
#"myusername",
#"launchctl",
#"load",
#"/Library/LaunchAgents/com.google.keystone.agent.plist"];
[task setArguments: arguments];
NSFileHandle * read = [pipe fileHandleForReading];
[task launch];
[task waitUntilExit];
NSData * dataRead = [read readDataToEndOfFile];
NSString * output = [[NSString alloc] initWithData:dataRead encoding:NSUTF8StringEncoding];
NSLog(#"output: %#", output);
After processing I receive an error below:
/bin/sh: sudo: No such file or directory
I've found out solution:
arguments = #[#"-c",
#"sudo -u myusername launchctl load /Library/LaunchAgents/com.google.keystone.agent.plist"];

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];

How to use NSTask with pbcopy?

I am a beginner and I have a problem. I would like to use NSTask with the command "pbcopy". I tried this but it seems that it doesn't work :
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/echo"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects: #"my-text-to-copy", #"| pbcopy", nil];
[task setArguments: arguments];
[task launch];
Any ideas ? Thanks.
It works fine :
NSTask *task = [[NSTask alloc] init];
NSPipe *pipe;
pipe = [NSPipe pipe];
task = [[NSTask alloc] init];
[task setLaunchPath:#"/bin/echo"];
[task setStandardOutput:pipe]; // write to pipe
[task setArguments: [NSArray arrayWithObjects: #"tmp", nil]];
[task launch];
[task waitUntilExit];
task = [[NSTask alloc] init];
[task setLaunchPath:#"/usr/bin/pbcopy"];
[task setStandardInput:pipe]; // read from pipe
[task launch];
[task waitUntilExit];
The pipe ("|") is a feature of the shell, not an argument to the command you're using. You have to use two NSTasks, one for echo and one for pbcopy and set up an NSPipe between them.
Btw, I'm assuming that you're just using this as an example. Otherwise it would be much simpler to use NSPasteboard for this.

NSTask to call command top. Getting Error

intelligent people!
Thanks so much for checking out my post.
Right now I'm running this:
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: #"/usr/bin/top"];
NSArray *arguments;
arguments = [NSArray arrayWithObjects: #"-stats", #"pid,cpu", nil];
[task setArguments: arguments];
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
//The magic line that keeps your log where it belongs
[task setStandardInput:[NSPipe pipe]];
NSFileHandle *file;
file = [pipe fileHandleForReading];
[task launch];
and it is giving me the error:
Error opening terminal: unknown.
Any clues? Thanks again!
It seems as if it was my arguments:
arguments = [NSArray arrayWithObjects: #"-s", #"1",#"-l",#"3600",#"-stats",#"pid,cpu,time,command", nil];
Thanks!