2011-06-14 214 views
5

我目前正试图围绕洞NSTask,NSPipe,NSFileHandle业务。所以我想我写了一个小工具,它可以编译和运行C代码。我也希望能够将stdout和stdin重定向到文本视图。发布后写入NSTasks标准输入

这是我到目前为止。 我使用的代码从这个帖子来重定向我的标准输入输出:What is the best way to redirect stdout to NSTextView in Cocoa?

NSPipe *inputPipe = [NSPipe pipe]; 
// redirect stdin to input pipe file handle 
dup2([[inputPipe fileHandleForReading] fileDescriptor], STDIN_FILENO); 
// curInputHandle is an instance variable of type NSFileHandle 
curInputHandle = [inputPipe fileHandleForWriting]; 

NSPipe *outputPipe = [NSPipe pipe]; 
NSFileHandle *readHandle = [outputPipe fileHandleForReading]; 
[readHandle waitForDataInBackgroundAndNotify]; 
// redirect stdout to output pipe file handle 
dup2([[outputPipe fileHandleForWriting] fileDescriptor], STDOUT_FILENO); 

// Instead of writing to curInputHandle here I would like to do it later 
// when my C program hits a scanf 
[curInputHandle writeData:[@"123" dataUsingEncoding:NSUTF8StringEncoding]]; 

NSTask *runTask = [[[NSTask alloc] init] autorelease]; 
[runTask setLaunchPath:target]; // target was declared earlier 
[runTask setArguments:[NSArray array]]; 
[runTask launch]; 

NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 
[center addObserver:self selector:@selector(stdoutDataAvailable:) name:NSFileHandleReadCompletionNotification object:readHandle]; 

而这里stdoutDataAvailable方法

- (void)stdoutDataAvailable:(NSNotification *)notification 
{ 
    NSFileHandle *handle = (NSFileHandle *)[notification object]; 
    NSString *str = [[NSString alloc] initWithData:[handle availableData] encoding:NSUTF8StringEncoding]; 
    [handle waitForDataInBackgroundAndNotify]; 
    // consoleView is an NSTextView 
    [self.consoleView setString:[[self.consoleView string] stringByAppendingFormat:@"Output:\n%@", str]]; 
} 

这个计划是工作就好了。它正在运行C程序,将stdout打印到我的文本视图并从我的inputPipe中读取“123”。正如我在上面的评论中指出的那样,我希望在任务需要时运行时提供输入。

所以现在有两个问题。

  1. 当有人试图从我的inputPipe中读取数据时,有没有办法获得通知?
  2. 如果1的答案是否定的,我可以尝试一种不同的方法吗?也许使用NSTask以外的类?

任何帮助,示例代码,其他资源的链接,高度赞赏!

回答

2

我不确定您是否可以在NSPipe上检测到“拉”。我有一个含糊不清的感觉,轮询写入可用性select()或使用kqueue查找您的NSFileHandle的底层文件描述符上的I/O可用性事件可能会有诀窍,但我不太熟悉如何使用这些设备这条路。

你是否必须支持任意C程序,还是它是一个特殊的守护进程或你开发的东西?你可以在outputPipe上查看关于outputPipe反馈的请求,或者在你发现你想要发送的内容时,只需要将它输入到inputPipe,然后让C程序在准备就绪时使用它;如果它是你自己的程序,你可以在outputPipe上查看反馈请求。如果这是别人的代码,你可以使用一个链接时方法(因为它的代码你编译)像附录A-4的描述的挂钩scanf和朋友:

http://www.cs.umd.edu/Library/TRs/CS-TR-4585/CS-TR-4585.pdf

它的要点是用你自定义的I/O函数(它可能发送一些sigil到你的应用程序表明它们需要输入),将它链接到构建的程序中,设置一个环境变量(DYLD_BIND_AT_LAUNCH=YES)作为启动任务,并运行它。一旦你有了这些钩子,你可以为你的主机程序提供任何你想要的便利。

+0

感谢您的回答乔。我最近再次处理这个问题,并得到它的工作(不完全是我想要的)。我为命令行程序创建了一个小GUI。当我运行命令行程序时,我将stdout打印到NSTextView,用户可以使用NSTextField提供输入。问题是用户可以写入stdin,即使程序不需要任何输入。这就是为什么如果每次子进程尝试从标准输入读取时都可以得到通知,那将是完美的。回答你的问题:是的,我想支持任意C程序。 – Janek 2011-06-28 00:37:42

+0

我将深入研究select()和kqueue。看起来很有趣。自定义I/O功能也可能工作,但我不想使用它们。 – Janek 2011-06-28 00:46:08

+2

我不完全确定,在一般情况下,您是否可以知道程序是否需要输入。请记住,还有一些程序使用例如程序监视输入管道的活动。选择或kqueue(再次打招呼!)。作为一个方面说明,如果你想要你可以使用单一的NSTextView的输入和输出(见[PseudoTTY项目](http://amath.colorado.edu/pub/mac/programs/)一些稍旧,但可能仍然有用的源代码)。 FWIW,我相信即使'Terminal.app'也会乐于接受输入而不关心正在运行的程序是否需要它,对吗? – 2011-06-28 03:03:50