grep
)?#1 楼
您可以使用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
评论
是的,'参数= [NSArray arrayWithObjects:@“-e”,@“ foo”,@“ bar.txt”,nil];“
–戈登·威尔逊
09年1月5日在8:47
您的答案有一个小故障。 NSPipe具有一个缓冲区(在操作系统级别设置),在读取时将刷新该缓冲区。如果缓冲区已满,NSTask将挂起,您的应用也将无限期挂起。没有错误消息出现。如果NSTask返回大量信息,则可能发生这种情况。解决方案是使用NSMutableData * data = [NSMutableData dataWithCapacity:512];。然后,同时([task isRunning]){[数据appendData:[文件readDataToEndOfFile]]; }。我“相信”您应该再添加一个[data appendData:[file readDataToEndOfFile]];在while循环退出之后。
–戴夫
2011-09-27 22:49
除非您执行此操作,否则错误不会出现(它们只会在日志中显示):[task setStandardError:pipe];
–迈克·斯普拉格(Mike Sprague)
2012年8月7日在22:32
这可以使用ARC和Obj-C数组文字进行更新。例如。 pastebin.com/sRvs3CqD
– bames53
2013年9月25日23:39
传送错误也是一个好主意。 task.standardError =管道;
–vqdave
14-10-20在23:44
#2 楼
肯特的文章给了我一个新的主意。此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;
}
评论
这可以很好地处理大多数情况,但是如果以循环方式运行它,则由于打开的文件句柄过多,最终会引发异常。可以通过添加以下内容来修复:[file closeFile];在readDataToEndOfFile之后。
–大卫·斯坦(David Stein)
16年5月15日在19:54
@DavidStein:我认为使用autoreleasepool来包装runCommand方法似乎不是。实际上,以上代码也没有考虑非ARC。
– Kenial
16年5月17日在0:39
@Kenial:哦,这是一个更好的解决方案。它还会在离开范围时立即释放资源。
–大卫·斯坦(David Stein)
16年5月19日在7:02
/ bin / ps:不允许操作,我没有获得任何成功,铅?
– Naman Vaishnav
18-2-23在12:32
#3 楼
本着共享的精神...这是我经常使用的运行shell脚本的方法。您可以将脚本添加到产品包中(在构建的复制阶段),然后
脚本被读取并在运行时运行。注意:此代码在privateFrameworks子路径中查找脚本。主机以rsync到...),而无需重新编译应用程序,而只是编辑捆绑软件中的shell脚本。
br />
如果您正在使用NSTask通过bash运行命令行实用程序,那么您需要包括以下魔术行以使NSLog正常工作:
/>在上下文中:
//------------------------------------------------------
-(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);
}
//------------------------------------------------------
这里是一个解释:http://www.cocoadev.com/index.pl?NSTask
评论
说明链接已死。
–珍妮
2014年7月8日在4:06
我想运行此命令“ system_profiler SPApplicationsDataType -xml”,但出现此错误“无法访问启动路径”
–Vikas Bansal
15年7月29日在12:20
#4 楼
在Swift中的操作方法如下:Swift 3.0的更改:
NSPipe
已重命名Pipe
NSTask
已重命名Process
这是基于上面inkit的Objective-C答案。他在
NSString
上将其写为类别-对于Swift来说,它成为
String
的扩展。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?
,但是使用起来很尴尬,并且没有用处,因为它不太可能发生。评论
真是一种优雅的方式!这个答案应该有更多的支持。
–薛宇
17年2月6日,下午3:17
如果您不需要输出。在runCommand方法的前面或上方添加@discardableResult参数。这将使您无需将其放在变量中即可调用该方法。
–劳埃德·凯伊泽(Lloyd Keijzer)
19年8月3日,下午5:21
让结果= String(字节:fileHandle.readDataToEndOfFile(),编码:String.Encoding.utf8)是可以的
–cleexiang
19年10月7日15:38
#5 楼
Objective-C(有关Swift,请参见下文)清理了最佳答案中的代码,使其更具可读性,减少了重复性,并添加了单行方法的优点,并使其成为NSString类别br />
@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];
希望对您和将来的我都一样有用。 (嗨,你好!)
用法:
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)
}
}
评论
确实,您的代码对我非常有用!我将其更改为Swift,并将其发布为下面的另一个答案。
– ElmerCat
2015年8月27日,下午3:14
#6 楼
如果您并不是真的在寻找一种Objective-C特定的方式,则fork,exec和wait应该可以工作。fork
创建当前正在运行的程序的副本,exec
用新的替换当前正在运行的程序,wait
等待子进程退出。例如(不进行任何错误检查):命令,就像您从Shell的命令行中键入命令一样。它比较简单,但是您对情况的控制却很少。 ,因此您应该在任何符合POSIX的系统上使用它们。评论
我知道这是一个非常古老的答案,但是我需要说:这是使用trheads处理执行的一种出色方法。唯一的缺点是它将创建整个程序的副本。因此,对于可可应用程序,我将与@GordonWilson一起使用以获得更好的方法,如果我在命令行应用程序上工作,则这是最佳方法。谢谢(对不起,我的英语不好)
– Nicos Karalis
13年2月17日在21:26
#7 楼
还有一个很好的旧POSIX系统(“ echo -en'\ 007'”);评论
不要运行此命令。 (以防您不知道此命令的作用)
–贾斯汀
09年12月8日在23:18
将其更改为更安全的声音……(发出哔哔声)
–nes1983
09年12月8日23:23
这不会在控制台中引发错误吗?检测到错误的NSStringEncoding值0x0000。假设NSStringEncodingASCII。在不久的将来将停止这种兼容性映射行为。
– cwd
2011-12-17 18:47
嗯也许您必须两次转义反斜杠。
–nes1983
2011-12-17 19:37
只需运行/ usr / bin / echo之类的东西。 rm -rf很苛刻,控制台中的unicode仍然很烂:)
–马克西姆·维克斯勒(Maxim Veksler)
17年1月3日在15:57
#8 楼
我写了这个“ C”函数,是因为NSTask
令人讨厌。 > NSString * runCommand(NSString* c) {
NSString* outP; FILE *read_fp; char buffer[BUFSIZ + 1];
int chars_read; memset(buffer, '#define $UTF8(A) ((NSString*)[NSS stringWithUTF8String:A])
', 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 ..
…
几年后,
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]; });
}
评论
在发生任何错误时,outP是未定义的,在sizeof(ssize_t)!= sizeof(int)的任何体系结构上,chars_read对于fread()的返回值而言太小,如果我们想要的输出多于BUFSIZ字节怎么办?如果输出不是UTF-8怎么办?如果pclose()返回错误怎么办?我们如何报告fread()错误?
– ObjectiveC代码
2014年3月11日上午10:45
@ ObjectiveC-oder D'oh-我不知道。请告诉我(如在..编辑后)!
–亚历克斯·格雷
2014年12月2日于1:30
#9 楼
除了上述几个出色的答案外,我还使用以下代码在后台处理命令的输出,并避免了[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];
}
评论
对我而言,造成所有差异的那一行是[self performSelectorInBackground:@selector(collectTaskOutput :) withObject:file];
– neowinston
19年5月1日在14:06
#10 楼
Custos Mortem说:我感到惊讶的是,没有人真正遇到阻塞/非阻塞呼叫问题有关
NSTask
的呼叫问题,请阅读以下内容:asynctask.m-示例代码,显示了如何实现异步stdin,stdout和stderr流以使用NSTask处理数据
asynctask.m的源代码可在GitHub上获得。
评论
查看我为非阻塞版本所做的贡献
–Guruniverse
19年5月3日,9:42
#11 楼
或者由于目标C只是在顶部带有OO层的C,您可以使用posix转换部分:>
#12 楼
如果Terminal命令需要管理员特权(又名sudo
),请改用AuthorizationExecuteWithPrivileges
。 > 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);
评论
自OS X 10.7起正式弃用
–山姆·沃什伯恩(Sam Washburn)
2012年12月16日上午9:58
..但无论如何它都可以继续工作,因为这是唯一的方法,我相信许多安装程序都依赖它。
–托马斯·坦佩尔曼(Thomas Tempelmann)
16 Dec 15'在11:34
评论
我只是在说明一个明显的事实:使用沙箱,您不能只是启动不在沙箱中的应用,并且需要您对其进行签名以允许此应用@ Daij-Djan根本不是真的,至少在macOS中不是。沙盒式macOS应用程序可以在grep所在的/ usr / bin等位置运行任何二进制文件。
否。请证明我错了;)在ist nstask上将无法运行沙箱中未运行的任何内容。