【问题标题】:NSTask to execute a find command with argumentsNSTask 执行带有参数的查找命令
【发布时间】:2016-02-27 14:26:50
【问题描述】:

我正在尝试从 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/ {} +

【问题讨论】:

  • task.arguments 给出正确答案了吗?

标签: objective-c macos nstask


【解决方案1】:

如果您打算使用/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!

【讨论】:

  • 优秀的答案帮助我了解了很多关于 Unix 中的 find 命令。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-31
  • 1970-01-01
相关资源
最近更新 更多