2016-02-27 76 views
0

我试图从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/ {} + 
+0

'task.arguments'是否给出正确答案? – matt

回答

3

如果你要使用/bin/bash作为任务的启动路径,那么你必须使用一个参数列表,将工作,如果你调用/bin/bash的命令,而不是命令您可以输入输入/bin/bash命令提示符。他们是两个不同的东西。

也就是说,你的代码是在外壳大致做了以下命令相同:

/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)并将参数传递给该文件。没有壳参与,所以没有任何参数的shell解释的风险。

所以:

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!

+0

非常好的答案,这有助于我在Unix中了解很多关于find命令的知识。非常感谢你! – BlackM