如何从Objective-C Cocoa应用程序执行终端命令(如grep) ?
当前回答
你可以使用NSTask。下面是一个运行'/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);
nspope和NSFileHandle用于重定向任务的标准输出。
有关在Objective-C应用程序中与操作系统交互的更详细信息,您可以查看Apple开发中心的这篇文档:与操作系统交互。
编辑:包括修复NSLog问题
如果你正在使用NSTask通过bash运行命令行实用程序,那么你需要包括这条神奇的行来保持NSLog工作:
//The magic line that keeps your log where it belongs
task.standardOutput = pipe;
解释在这里:https://web.archive.org/web/20141121094204/https://cocoadev.com/HowToPipeCommandsWithNSTask
其他回答
Objective-C (Swift见下文)
清理顶部答案中的代码,使其更具可读性,减少冗余,增加单行方法的好处,并使其成为NSString类别
@interface NSString (ShellExecution)
- (NSString*)runAsCommand;
@end
实现:
@implementation NSString (ShellExecution)
- (NSString*)runAsCommand {
NSPipe* pipe = [NSPipe pipe];
NSTask* task = [[NSTask alloc] init];
[task setLaunchPath: @"/bin/sh"];
[task setArguments:@[@"-c", [NSString stringWithFormat:@"%@", self]]];
[task setStandardOutput:pipe];
NSFileHandle* file = [pipe fileHandleForReading];
[task launch];
return [[NSString alloc] initWithData:[file readDataToEndOfFile] encoding:NSUTF8StringEncoding];
}
@end
用法:
NSString* output = [@"echo hello" runAsCommand];
如果你在输出编码方面有问题:
// Had problems with `lsof` output and Japanese-named files, this fixed it
NSString* output = [@"export LANG=en_US.UTF-8;echo hello" runAsCommand];
希望它对你和未来的我一样有用。(嗨,你!)
斯威夫特4
下面是一个使用管道、进程和字符串的Swift示例
extension String {
func run() -> String? {
let pipe = Pipe()
let process = Process()
process.launchPath = "/bin/sh"
process.arguments = ["-c", self]
process.standardOutput = pipe
let fileHandle = pipe.fileHandleForReading
process.launch()
return String(data: fileHandle.readDataToEndOfFile(), encoding: .utf8)
}
}
用法:
let output = "echo hello".run()
验尸官说:
我很惊讶没有人真正陷入阻塞/非阻塞调用问题
关于NSTask的阻塞/非阻塞调用问题,请阅读以下内容:
asynctask。m——示例代码,展示了如何实现异步stdin, stdout和stderr流处理数据与NSTask
asynctask的源代码。m可以在GitHub上找到。
如果终端命令需要管理员权限(又名sudo),请使用AuthorizationExecuteWithPrivileges代替。 下面将创建一个名为“com.stackoverflow”的文件。test是根目录“/System/Library/Caches”。
AuthorizationRef authorizationRef;
FILE *pipe = NULL;
OSStatus err = AuthorizationCreate(nil,
kAuthorizationEmptyEnvironment,
kAuthorizationFlagDefaults,
&authorizationRef);
char *command= "/usr/bin/touch";
char *args[] = {"/System/Library/Caches/com.stackoverflow.test", nil};
err = AuthorizationExecuteWithPrivileges(authorizationRef,
command,
kAuthorizationFlagDefaults,
args,
&pipe);
除了上面的几个出色的答案之外,我还使用以下代码在后台处理命令的输出,并避免[file readDataToEndOfFile]的阻塞机制。
- (void)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];
[self performSelectorInBackground:@selector(collectTaskOutput:) withObject:file];
}
- (void)collectTaskOutput:(NSFileHandle *)file
{
NSData *data;
do
{
data = [file availableData];
NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] );
} while ([data length] > 0); // [file availableData] Returns empty data when the pipe was closed
// Task has stopped
[file closeFile];
}
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 then have 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);
}
//------------------------------------------------------
编辑:包括修复NSLog问题
如果你正在使用NSTask通过bash运行命令行实用程序,那么你需要包括这条神奇的行来保持NSLog工作:
//The magic line that keeps your log where it belongs
[task setStandardInput:[NSPipe pipe]];
时代背景:
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
//The magic line that keeps your log where it belongs
[task setStandardInput:[NSPipe pipe]];
解释在这里:http://www.cocoadev.com/index.pl?NSTask
推荐文章
- 访问限制:'Application'类型不是API(必需库rt.jar的限制)
- 为什么在Mac OS X v10.9 (Mavericks)的终端中apt-get功能不起作用?
- “你有邮件”的消息在终端,os X
- 为什么ARC仍然需要@autoreleasepool ?
- Mac OS X中的环境变量
- 首先添加一个UIView,甚至是导航栏
- 我如何改变UIButton标题颜色?
- 如何从UIImage (Cocoa Touch)或CGImage (Core Graphics)获取像素数据?
- 如何从macOS完全卸载蟒蛇
- iOS -构建失败,CocoaPods无法找到头文件
- Xcode 4挂在“附加到(应用程序名称)”
- 为什么单元测试中的代码不能找到包资源?
- 如何配置Mac OS X术语,使git有颜色?
- 以编程方式创建segue
- 在Objective-C中@synchronized如何锁定/解锁?