簡體   English   中英

Xcode Cocoa目標C使用NSTask執行“查找” Shell命令返回語法

[英]Xcode Cocoa Objective C Using NSTask to Execute “Find” Shell Command Returns Syntax

我正在使用Xcode 6.4嘗試執行find命令。 該應用程序是find命令的包裝器。 我們有一個SAN,Finder不會搜索,但是find命令會搜索SAN,我不想討論SAN的問題。

我最初是在幾分鍾內用Swift編寫應用程序的。 不幸的是,它將無法部署到OSX 10.8,因此我着手在Objective C中進行重寫。

我懷疑我遇到的問題與構造arguments數組的方式有關。 在谷歌搜索3天后,我遇到的唯一示例都具有用於參數的硬編碼字面量,在現實世界中,我們使用變量。

這是單擊“搜索”按鈕時關聯的基本代碼。

NSString* newShell = @"-c";
NSString* commandFind = @"find";
NSString* optionName = @"-iname";
NSString* searchFor = @"\"";
NSString* searchPath = @"\"";
searchPath = [searchPath stringByAppendingString:_searchPathOutlet.stringValue];
searchPath = [searchPath stringByAppendingString:@"\""];
NSString* searchWildCard = @"*";
NSString* searchWord = _searchWordsOutlet.stringValue;
searchFor = [searchFor stringByAppendingString:searchWildCard];
searchFor = [searchFor stringByAppendingString:searchWord];
searchFor = [searchFor stringByAppendingString:searchWildCard];
searchFor = [searchFor stringByAppendingString:@"\""];
NSLog(@"%@",searchPath); //debug
NSLog(@"%@",searchFor); //debug
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/bin/sh"];
NSArray *arguments = [NSArray arrayWithObjects: newShell, commandFind, searchPath, optionName, searchFor, nil];
NSString *stringRep = [NSString stringWithFormat:@"%@",arguments]; //debug
NSLog(@"%@",stringRep); //debug
[task setArguments:arguments];
NSPipe* pipe = [NSPipe pipe];
[task setStandardOutput:pipe];
[task launch];
[task waitUntilExit]; // Alternatively, make it asynchronous.
NSData *outputData = [[pipe fileHandleForReading] readDataToEndOfFile];
NSString *outputString = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding];
_searchResultsOutlet.string = outputString;



The output is as follows:
2015-07-16 12:09:39.141 APSAETVSANSearch[2716:68456] "/Users/test/Downloads"
2015-07-16 12:09:39.141 APSAETVSANSearch[2716:68456] "*adobe*"
2015-07-16 12:09:39.141 APSAETVSANSearch[2716:68456] (
"-c",
find,
"\"/Users/test/Downloads\"",
"-iname",
"\"*adobe*\""
)
usage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]
find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]

我同意您和trojanfoe的意見。 我是Objective-c的新手,與其他語言相比,我覺得語法有些奇怪,但是下面列出了一些工作代碼。 我想將功能doSearch作為異步線程中的回調運行。 我熟悉Microsoft的線程,該線程與使用委托給線程的回調函數(例如searchPathURL,searchWords,textView)傳遞“線程安全”參數有關,但似乎找不到適合Google的示例。 這是我的職能,您可以讓我開始嗎?

- (IBAction)buttonSearch_Click:(id)sender {
doSearch(directoryURL, _searchWordOutlet.stringValue, _textViewOutlet);
}

void doSearch(NSURL *searchPathURL, NSString *searchWords, NSTextView *textView){
NSArray *keys = [NSArray arrayWithObjects:
NSURLIsDirectoryKey, NSURLIsPackageKey, NSURLLocalizedNameKey, nil];
NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager]
enumeratorAtURL:directoryURL
includingPropertiesForKeys:keys
options:(NSDirectoryEnumerationSkipsHiddenFiles)
errorHandler:^(NSURL *url, NSError *error) {
 // Handle the error.
 // Return YES if the enumeration should continue after the error.
 return YES;
}
];
for (NSURL *url in enumerator) {
// Error-checking is omitted for clarity.

NSNumber *isDirectory = nil;
[url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:NULL];

if ([isDirectory boolValue]) {
NSString *localizedName = nil;
[url getResourceValue:&localizedName forKey:NSURLLocalizedNameKey error:NULL];

NSNumber *isPackage = nil;
[url getResourceValue:&isPackage forKey:NSURLIsPackageKey error:NULL];

if ([isPackage boolValue]) {
//Its a package
//NSLog(@"Package at %@", localizedName);
}
else {
//Its a directory
//NSLog(@"Directory at %@", localizedName);
}
}
else {
//Its a file
NSString *searchPath = [url.path lowercaseString];
NSString *searchText = [searchWords lowercaseString];
if ([searchPath containsString:searchText]) {
NSLog(@"%@", url.path);
[textView insertText:url.path];
[textView insertText:@"\n"];
}
}
}
}

當使用/bin/sh -c <command> ,命令必須是一個參數。 也就是說,在外殼程序上,您不能執行以下操作:

/bin/sh -c find "/Users/test/Downloads" -iname "*adobe*"

你必須做:

/bin/sh -c 'find "/Users/test/Downloads" -iname "*adobe*"'

因此,只需制作一個字符串,其內容等效於:

NSString* command = @"find \"/Users/test/Downloads\" -iname \"*adobe*\"";

並使用等效於@[ @"-c", command ]的參數數組。

另外,如果不需要外殼程序來處理字符串(在您的示例中也不需要),則只需將任務的啟動路徑設置為@"/usr/bin/find"並將參數設置為@[ @"/Users/test/Downloads", @"-iname", @"*adobe*" ] 不需要時使用外殼只會增加危險和效率低下。 例如,如果用戶在文本字段中輸入“ $(rm -rf〜)”,則當您運行任務時,他們將非常不滿意。 破壞性較小,但如果目錄路徑或搜索詞包含雙引號( " )字符,則破壞可能性更大。

綜上所述,我同意trojanfoe的觀點,您應該以編程方式執行此操作,而不是啟動子進程。 如果由於某些原因NSDirectoryEnumerator不起作用,則可以使用POSIX / BSD API。


更新以回應您更新的問題:

要在后台運行任務,您可以使用Grand Central Dispatch(GCD)。 例如,您的-buttonSearch_Click:方法可以這樣編寫:

- (IBAction)buttonSearch_Click:(id)sender {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        doSearch(directoryURL, _searchWordOutlet.stringValue, _textViewOutlet);
    });
}

但是,您不能從后台線程更新GUI。 因此,您的doSearch()函數需要將對文本視圖的任何操作分流回主線程。 它可以使用如下代碼執行此操作:

dispatch_async(dispatch_get_main_queue(), ^{
    [textView insertText:url.path];
    [textView insertText:@"\n"];
});

順便說一句,您對搜索詞是否在路徑中的檢查與您最初使用的find命令所執行的檢查不同。 您正在檢查整個路徑,包括父目錄,而find命令僅檢查每個項目的文件名。 您可以通過請求NSURLlastPathComponent而不是其path來獲取文件名。

另外,如果一個字符串包含另一個做一個不區分大小寫的檢查,你不應該小寫boths字符串,然后調用-containsString: 您應該只使用-localizedCaseInsensitiveContainsString:而不用手動-localizedCaseInsensitiveContainsString:大小寫。 (或者,如果不想讓區域設置不區分大小寫,則可以使用-rangeOfString:options:NSCaseInsensitiveSearch作為選項。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM