![](/img/trans.png)
[英]Xcode Cocoa Objective C Using NSTask to Execute “Find” Shell Command Returns Syntax
[英]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
作為命令而不是作為/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];
但是,如果路徑中帶有引號,則無濟於事。 更糟糕的是,如果路徑中包含$或`之類的特殊字符,則無濟於事。 該路徑最終可能會被外殼程序解釋並執行子命令。 例如,其中包含$(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!
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.