我正在使用Xcode 6.4尝试执行find命令。该应用程序是find命令的包装器。我们有一个查找器不会搜索的SAN,但是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函数作为回调。我熟悉微软的线程涉及使用委托将“线程安全”参数传递给线程的回调函数(例如searchPathURL,searchWords,textView),但似乎无法通过谷歌找到合适的示例。这是我的功能,你可以让我开始吗?
- (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"];
}
}
}
}
答案 0 :(得分:2)
使用/bin/sh -c <command>
时,命令必须是一个参数。也就是说,在shell中,你无法做到:
/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 ]
的参数数组。
或者,如果您不需要shell来处理字符串(在您的示例中,您不必),您应该将任务的启动路径设置为{{1并将参数设置为@"/usr/bin/find"
。当你不需要它时使用shell只会增加危险和低效率。例如,如果您的用户输入&#34; $(rm -rf~)&#34;在您的文本字段中,当您运行任务时,他们会非常不满意。如果目录路径或搜索词包含双引号(@[ @"/Users/test/Downloads", @"-iname", @"*adobe*" ]
)字符,则破坏性较小但更有可能。
所有这些都说,我同意trojanfoe你应该以编程方式执行此操作,而不是启动子进程。如果"
由于某种原因不起作用,您可以使用POSIX / BSD API。
更新以回应您更新的问题:
要在后台运行任务,您可以使用Grand Central Dispatch(GCD)。例如,您的NSDirectoryEnumerator
方法可以这样写:
-buttonSearch_Click:
但是,您无法从后台线程更新GUI。因此,您的- (IBAction)buttonSearch_Click:(id)sender {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
doSearch(directoryURL, _searchWordOutlet.stringValue, _textViewOutlet);
});
}
函数需要将文本视图的任何操作分流回主线程。它可以使用以下代码执行此操作:
doSearch()
顺便说一句,检查搜索词是否在路径中与您开始使用的dispatch_async(dispatch_get_main_queue(), ^{
[textView insertText:url.path];
[textView insertText:@"\n"];
});
命令不一样。您正在检查整个路径,包括父目录,而find
命令仅检查每个项目的文件名。您可以通过请求find
而不是NSURL
来获取lastPathComponent
中的文件名。
此外,要进行不区分大小写的检查,如果一个字符串包含另一个字符串,则不应低位两个字符串,然后调用path
。您应该只使用-containsString:
而不用手动降低外壳。 (或者,如果您不希望区分大小写不区分大小写,则可以将-localizedCaseInsensitiveContainsString:
与-rangeOfString:options:
一起用于选项。)