如何grep
从Objective-C Cocoa应用程序执行终端命令(如)?
你可以用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);
NSPipe
并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
本着共享的精神...这是我经常用来运行shell脚本的方法.您可以将脚本添加到产品包中(在构建的复制阶段),然后在运行时读取并运行脚本.注意:此代码在privateFrameworks子路径中查找脚本.警告:这可能会对部署的产品造成安全风险,但对于我们的内部开发,它是一种简单的方法来定制简单的东西(比如rsync到哪个主机......)而无需重新编译应用程序,只需编辑捆绑包中的shell脚本.
//------------------------------------------------------ -(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
肯特的文章给了我一个新的想法.这个runCommand方法不需要脚本文件,只需按行运行命令:
- (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; }
您可以像这样使用此方法:
NSString *output = runCommand(@"ps -A | grep mysql");
Swift 3.0的变化:
NSPipe
已重命名Pipe
NSTask
已重命名Process
这是基于上面的墨水的Objective-C答案.他写了它作为一个类别上NSString
-对于斯威夫特,它成为一个扩展的String
.
extension String { func runAsCommand() -> String { let pipe = Pipe() let task = Process() task.launchPath = "/bin/sh" task.arguments = ["-c", String(format:"%@", self)] task.standardOutput = pipe let file = pipe.fileHandleForReading task.launch() if let result = NSString(data: file.readDataToEndOfFile(), encoding: String.Encoding.utf8.rawValue) { return result as String } else { return "--- Error running command - Unable to initialize string from file data ---" } } }
let input = "echo hello" let output = input.runAsCommand() print(output) // prints "hello"
要不就:
print("echo hello".runAsCommand()) // prints "hello"
@IBAction func toggleFinderShowAllFiles(_ sender: AnyObject) { var newSetting = "" let readDefaultsCommand = "defaults read com.apple.finder AppleShowAllFiles" let oldSetting = readDefaultsCommand.runAsCommand() // Note: the Command results are terminated with a newline character if (oldSetting == "0\n") { newSetting = "1" } else { newSetting = "0" } let writeDefaultsCommand = "defaults write com.apple.finder AppleShowAllFiles \(newSetting) ; killall Finder" _ = writeDefaultsCommand.runAsCommand() }
请注意,Process
结果为已读从Pipe
是一个NSString
对象.它可能是一个错误字符串,它也可以是一个空字符串,但它应该始终是一个NSString
.
所以,只要它不是nil,结果就可以转换为Swift String
并返回.
如果由于某种原因根本NSString
无法从文件数据初始化,则该函数返回错误消息.该函数可能已被编写为返回一个可选项String?
,但使用起来很难,并且不会起到有用的作用,因为它不太可能发生.
清除了最顶层答案中的代码,使其更具可读性,更少冗余,增加了单行方法的优点并制作成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];
希望它对你和对我未来都有用.(你好!)
这里有一个例子斯威夫特利用的Pipe
,Process
和String
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()
fork,exec和wait应该工作,如果你不是真的在寻找Objective-C特定的方法.fork
创建当前正在运行的程序的副本,exec
用新的程序替换当前运行的程序,并wait
等待子进程退出.例如(没有任何错误检查):
#include#include
pid_t p = fork(); if (p == 0) { /* fork returns 0 in the child process. */ execl("/other/program/to/run", "/other/program/to/run", "foo", NULL); } else { /* fork returns the child's PID in the parent. */ int status; wait(&status); /* The child has exited, and status contains the way it exited. */ } /* The child has run and exited by the time execution gets to here. */
还有系统,它运行命令,就好像你从shell的命令行输入命令一样.它更简单,但您对情况的控制较少.
我假设您正在使用Mac应用程序,因此这些链接是针对这些函数的Apple文档,但它们都是POSIX
,所以您应该在任何符合POSIX的系统上使用它们.
还有一个很好的旧POSIX 系统("echo -en'\ 007'");
我写了这个"C"函数,因为它NSTask
是令人讨厌的..
NSString * runCommand(NSString* c) { NSString* outP; FILE *read_fp; char buffer[BUFSIZ + 1]; int chars_read; memset(buffer, '\0', sizeof(buffer)); read_fp = popen(c.UTF8String, "r"); if (read_fp != NULL) { chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp); if (chars_read > 0) outP = $UTF8(buffer); pclose(read_fp); } return outP; } NSLog(@"%@", runCommand(@"ls -la /")); total 16751 drwxrwxr-x+ 60 root wheel 2108 May 24 15:19 . drwxrwxr-x+ 60 root wheel 2108 May 24 15:19 .. …
哦,为了完整/明确......
#define $UTF8(A) ((NSString*)[NSS stringWithUTF8String:A])
多年以后,C
对我来说仍然是一个令人眼花缭乱的混乱......并且对我纠正上述严重缺点的能力缺乏信心 - 我提供的唯一的橄榄枝是@ inket的答案,这是最简单的骨头,对于我的同伴纯粹主义者/诽谤者......
id _system(id cmd) { return !cmd ? nil : ({ NSPipe* pipe; NSTask * task; [task = NSTask.new setValuesForKeysWithDictionary: @{ @"launchPath" : @"/bin/sh", @"arguments" : @[@"-c", cmd], @"standardOutput" : pipe = NSPipe.pipe}]; [task launch]; [NSString.alloc initWithData: pipe.fileHandleForReading.readDataToEndOfFile encoding:NSUTF8StringEncoding]; }); }