当前位置:  开发笔记 > 编程语言 > 正文

从Cocoa应用程序执行终端命令

如何解决《从Cocoa应用程序执行终端命令》经验,为你挑选了8个好方法。

如何grep从Objective-C Cocoa应用程序执行终端命令(如)?



1> Gordon Wilso..:

你可以用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);

NSPipeNSFileHandle用于重定向任务的标准输出.

有关从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


你的答案有一个小问题.NSPipe有一个缓冲区(在OS级别设置),在读取时刷新.如果缓冲区填满,NSTask将挂起,您的应用程序也会无限期地挂起.不会出现任何错误消息.如果NSTask返回大量信息,就会发生这种情况.解决方案是使用`NSMutableData*data = [NSMutableData dataWithCapacity:512];`.然后,`while([task isRunning]){[data appendData:[file readDataToEndOfFile]]; }`.而且我"相信"你应该再有一个`[data appendData:[file readDataToEndOfFile]];`在while-loop退出之后.
除非你这样做(它们只是打印在日志中),否则不会出现错误:[task setStandardError:pipe];

2> kent..:

本着共享的精神...这是我经常用来运行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


解释链接已经死了.

3> Kenial..:

肯特的文章给了我一个新的想法.这个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");



4> ElmerCat..:
这是在Swift中如何做到这一点

Swift 3.0的变化:

NSPipe 已重命名 Pipe

NSTask 已重命名 Process


这是基于上面的墨水的Objective-C答案.他写了它作为一个类别NSString-对于斯威夫特,它成为一个扩展String.

extension String.runAsCommand() - > 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?,但使用起来很难,并且不会起到有用的作用,因为它不太可能发生.



5> inket..:

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

这里有一个例子斯威夫特利用的Pipe,ProcessString

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()


的确,你的代码对我非常有用!我将其更改为Swift并将其发布为下面的另一个答案.

6> Zach Hirsch..:

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的系统上使用它们.



7> nes1983..:

还有一个很好的旧POSIX 系统("echo -en'\ 007'");


不要运行这个命令.(如果你不知道这个命令是做什么的)
将它改成更安全的东西......(它发出哔哔声)

8> Alex Gray..:

我写了这个"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]; });
}

推荐阅读
帆侮听我悄悄说星星
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有