简体   繁体   中英

How do I capture output from a shell command?

Cocoa newbie warning!

I find the following shell command to be a nice way to determine if a process is running (1 = running, 0 = not running):

if [ $(ps -Ac | egrep -o 'ProcessName') ]; then echo 1; else echo 0; fi;

I can incorporate this into Cocoa with the "system" command:

system("if [ $(ps -Ac | egrep -o 'Finder') ]; then echo 1; else echo 0; fi;");

However, the output is directed to the run log, and I can't figure out how to capture the result (1 or 0) in my Cocoa code.

I tried implementing this with NSTask as follows:

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/sh"];
[task setArguments:[NSArray arrayWithObject:@"if [ $(ps -Ac | egrep -o 'Finder') ]; then echo 1; else echo 0; fi;"]];
NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput:pipe];
[task launch];
NSData *data = [[pipe fileHandleForReading] readDataToEndOfFile];
[task waitUntilExit];
[task release];
NSString *output = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog (@"%@", output);
[output release];

However, this generates the following error message:

if [ $(ps -Ac | egrep -o 'Finder') ]; then echo 1; else echo 0; fi;: No such file or directory

Can you please tell me how I can correctly implement this shell command in a way that allows me to capture the output (1 or 0) in code? (I am aware of other methods of determining whether a process is running, but part of the reason for my question is to learn how to implement shell scripts in general within Cocoa.)

Thank you very much for any help with this problem.

You're doing it wrong. Rather than asking "how do I run an external process to do X" you should ask "how do I write code to do X".

You don't need to use an external script to get a list of processes. In general, you should always try to use an API rather than launch an external task, for both performance and security reasons.

In this case the API you want is the the sysctl C interface.

JongAm Park has written an Objective-C wrapper for using sysctl to get a list of running processes. There are good points made in the comments on his post also.

Alternatively, you can see how Apple does it by looking at the source code for the ps command.

Rob, thank you for the general pointer about using a direct solution in code whenever possible. I looked through JongAm Park's wrapper for sysctl , as well as Apple's ps source code. It will take a while to incorporate, but I know now where to look for a direct solution to getting a list of processes.

shellter and tripleee, thank you for your suggestions concerning interacting with shell commands. Based on your suggestions, I got three methods to work (!):

Method 1 uses the system command's return code (no need for egrep -o ):

BOOL processIsRunning = system("ps -Ac | grep 'ProcessName' > /dev/null") == 0;

Method 2 uses NSTask with the shell -c option (note -c rather than -e ):

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/sh"];
[task setArguments:[NSArray arrayWithObjects:@"-c",@"ps -Ac | grep 'ProcessName'",nil]];
NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput:pipe];
[task launch];
NSData *data = [[pipe fileHandleForReading] readDataToEndOfFile];
[task waitUntilExit];
[task release];
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
BOOL processIsRunning = [result length] > 0;

Method 3 also uses NSTask with the shell -c option, but in this case, the command to be executed is another shell with its own -c option (this was the only way I could find after much trial and error to incorporate an if construct in the command to be executed; of course, it is way overkill for the current problem):

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/sh"];
[task setArguments:[NSArray arrayWithObjects:@"-c",@"/bin/sh -c 'if [ \"$(ps -Ac | egrep -o 'ProcessName')\" = \"ProcessName\" ]; then echo 1; else echo 0; fi;'",nil]];
NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput:pipe];
[task launch];
NSData *data = [[pipe fileHandleForReading] readDataToEndOfFile];
[task waitUntilExit];
[task release];
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
BOOL processIsRunning = [result intValue] == 1;

Thank you all for the wonderful help.

Here is a general answer not involving Cocoa at all:

If you want to get output from system() , don't use system() , use popen() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM