使用NSTask运行用户定义的命令

时间:2012-09-12 15:42:41

标签: objective-c cocoa nstask

我想执行用户指定的终端命令。例如,用户可能在文本字段中写killall "TextEdit"say "Hello world!",我想执行该命令。

NSTask是要走的路,除了我有两个问题:

首先:参数。现在我正在这样做:

NSArray* args = [commandString componentsSeparatedByString: @" "];
[task setArguments: [args subarrayWithRange: NSMakeRange(1, [args count] - 1)]]; // First one is the command name

这是这样做的吗?我不认为我有这个问题,但我看起来并不安全。想象一下:用户写killall 'Address Book'但命令接收为'AddressBook'?这不起作用。那么,我该怎么做呢?我怎样才能安全地解析参数?

第二:启动路径。只需要编写命令的名称,而不是完整的路径,它更加用户友好。所以我想支持它,这意味着以编程方式查找仅具有其名称的命令的完整路径。为此,我在NSTask上写了一个类别:

+ (NSString*)completePathForExec: (NSString*)exec
{
    NSTask* task = [[NSTask alloc] init];
    NSPipe* pipe = [[NSPipe alloc] init];

    NSArray* args = [NSArray arrayWithObject: exec];
    [task setLaunchPath: @"/usr/bin/which"];
    [task setArguments: args];
    [task setStandardOutput: pipe];
    [task setStandardError: pipe];

    [task launch];    
    [task waitUntilExit];

    NSFileHandle* file = [pipe fileHandleForReading];
    NSString* result = [[NSString alloc] initWithData: [file readDataToEndOfFile] encoding: NSASCIIStringEncoding];

    if ([result length]) {
        if ([result hasSuffix: @"\n"]) { result = [result substringWithRange: NSMakeRange(0, [result length] - 1)]; }

        return result;
    }
    else { return exec; }
}

这似乎运作良好。但是,我如何确定此路径:/usr/bin/which将始终有效?我的意思是:它会在10.6,10.7,10.8等工作吗?我认为一旦shell命令的路径随系统版本发生变化,我就遇到了问题,你永远不会太小心。

如果保证路径保持不变,那么这不是问题。如果它改变了,那我怎么知道'路径查找器的路径'?

1 个答案:

答案 0 :(得分:3)

如果不重新发明命令行解析轮,你会容易得多。但是,当然,沿着执行任意用户输入的代码的路线走下去是一个安全的噩梦(由于用户可以访问系统,因此可能只是直接运行终端)。

具体来说,让NSTask使用命令行选项包装其中一个shell的调用,以使其执行任意字符串。

sh -c "ls -alF"

这将允许您将路径传递给sh作为您的启动路径,该路径位于每个系统的固定位置。 @"-c"参数告诉sh将下一个参数解析为脚本,当然,下一个参数是用户输入的内容。

请注意,这也将为用户提供管道内容的功能。