2016-02-27 73 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