NSTask 执行带参数的查找命令
NSTask to execute a find command with arguments
我正在尝试从 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
作为命令,而不是像你这样的命令将作为 input 输入到 /bin/bash
命令提示符。它们是两个不同的东西。
也就是说,您的代码大致相当于 shell 处的以下命令:
/bin/bash find some path here -type f -name "*.m" -exec sed -i '' "s/NSLog/\/\/NSLog/" {} +
注意该命令开头的 /bin/bash
。另请注意,这是行不通的。最后,我特意将路径写为 "some path here" 并带有空格,以强调您的命令没有规定将其中包含空格的路径视为 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!
.
我正在尝试从 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
作为命令,而不是像你这样的命令将作为 input 输入到 /bin/bash
命令提示符。它们是两个不同的东西。
也就是说,您的代码大致相当于 shell 处的以下命令:
/bin/bash find some path here -type f -name "*.m" -exec sed -i '' "s/NSLog/\/\/NSLog/" {} +
注意该命令开头的 /bin/bash
。另请注意,这是行不通的。最后,我特意将路径写为 "some path here" 并带有空格,以强调您的命令没有规定将其中包含空格的路径视为 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!
.