NSTask 执行带有参数的查找命令



我正在尝试从NSTask执行终端命令,但似乎我做错了什么。

我试过这个:

  NSTask *task = [[NSTask alloc] init];
task.launchPath = @"/bin/bash";
NSString *arr = [NSString stringWithFormat:@"find %@ -type f -name "*.m" -exec sed -i '' "s/NSLog/\/\/NSLog/" {} +",path];
task.arguments = [arr componentsSeparatedByString:@" "];
[task launch];

论点有很多变化,但我无法让它工作。终端命令工作正常:

find /a/path/ -type f -name '*.m' -exec sed -i '' s/NSLog/\/\/NSLog/ {} +
如果要使用

/bin/bash 作为任务的启动路径,则必须使用参数列表,如果您调用 /bin/bash 作为命令,而不是作为/bin/bash命令提示符输入的命令,该列表将起作用。它们是两回事。

也就是说,您的代码在 shell 上执行的命令大致等效

/bin/bash find some path here -type f -name "*.m" -exec sed -i '' "s/NSLog///NSLog/" {} +

请注意该命令开头的/bin/bash。另请注意,这不起作用。最后,我故意将路径写成带有空格的"此处的路径",以强调您的命令没有规定将包含空格的路径视为find的单个参数。

如果要运行 /bin/bash 并向其传递字符串以解释为命令,则需要使用 -c 选项并将命令字符串作为该-c选项的单个参数传递。所以:

/bin/bash -c 'find some path here -type f -name "*.m" -exec sed -i "" "s/NSLog///NSLog/" {} +'

这解决了其中一个问题。它将对应于:

task.arguments = @[ @"-c", arr ];

这仍然不能解决路径问题。您可能会天真地尝试通过更改格式字符串以在%@格式说明符周围加上引号来解决这个问题,如下所示:

NSString *arr = [NSString stringWithFormat:@"find "%@" -type f -name "*.m" -exec sed -i '' "s/NSLog/\/\/NSLog/" {} +",path];

但是,如果路径中包含引号,则无济于事。更糟糕的是,如果路径中包含 $ 或 ' 等特殊字符,则无济于事。路径最终可能被 shell 解释并执行子命令。例如,包含$(rm -rf ~)的路径将是灾难性的。

您可以尝试用单引号引用它:

NSString *arr = [NSString stringWithFormat:@"find '%@' -type f -name "*.m" -exec sed -i '' "s/NSLog/\/\/NSLog/" {} +",path];

但是路径本身可以包含一个引号,这将结束引号,然后是特殊字符。恶意路径只是更改为 '$(rm -rf ~) .

可以

正确引用路径,但一般来说,在不必要的情况下使用 shell 是愚蠢的。

更好的方法是直接调用要运行的可执行文件(/usr/bin/find)并将参数传递给该可执行文件。不涉及外壳,因此没有对任何参数进行外壳解释的风险。

所以:

task.launchPath = @"/usr/bin/find";
task.arguments = @[ path, @"-type", @"f", @"-name", @"*.m", @"-exec", @"sed", @"-i", @"", @"s/NSLog/\/\/NSLog/", @"{}", @"+" ];

比这更好的是在代码中进行目录枚举 - 您在这里使用find - 因为它很容易。

NSURL* url = [NSURL fileURLWithPath:path];
NSFileManager* fm = [[NSFileMananger alloc] init];
// Consider passing NSDirectoryEnumerationSkipsPackageDescendants in the options
NSDirectoryEnumerator* e = [fm enumeratorAtURL:url includingPropertiesForKeys:nil options:0 errorHandler:nil];
for (NSURL* item in e)
{
    if ([item.pathExtension isEqualToString:@"m"])
    {
        // Use NSTask to run your /usr/bin/sed command on the item
    }
}

最后,您传递给sed的命令似乎不起作用。也许它需要另一个级别的转义(针对 C 编译器和sed本身),但避免将斜杠视为特殊字符更容易。如果要在替换中使用斜杠,则可以并且应该在模式周围使用不同的分隔符。例如:s!NSLog!//NSLog! .

最新更新