如果您打算使用/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) 并将参数传递给它。不涉及 shell,因此不存在 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!。