gpt4 book ai didi

macos - 从Cocoa应用程序控制交互式命令行实用程序-PTYS的麻烦

转载 作者:行者123 更新时间:2023-12-03 16:09:34 26 4
gpt4 key购买 nike

我想做什么

我的Cocoa应用程序需要运行一堆命令行程序。其中大多数是非交互性的,因此我使用一些命令行参数启动它们,它们执行任务,输出某些内容并退出。其中一个程序是交互式的,因此它会向stdout输出一些文本和提示,然后期望在stdin上输入,并且一直持续到您向其发送quit命令为止。

什么有效

只是将大量数据转储到stdout然后终止的非交互式程序比较简单:

  • 为stdout/stdin/stderr创建NSPipe
  • 用这些管道启动NSTask

  • 然后,要么
  • 获取管道另一端的NSFileHandle,以读取所有数据,直到流的末尾,并在任务结束时一次性处理数据

  • 或者
  • 从输出管道另一端的-fileDescriptor中获取NSFileHandle
  • 设置文件描述符以使用非阻塞模式
  • 使用dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, ...
  • 使用每个文件描述符创建GCD调度源
  • 恢复分发源并使用read()来处理向您抛出的数据
  • 继续执行直到任务结束,并且管道文件描述符报告EOF(read()报告读取了0个字节)

  • 什么不起作用

    两种方法都无法完全解决交互式工具的问题。显然,我迫不及待要退出该程序,因为它位于命令提示符下,除非我告知,否则永远不会退出。另一方面, NSPipe会缓冲数据,因此您将以缓冲区大小的块形式接收数据,除非CLI程序碰巧显式刷新了管道(在我的情况下不是)。初始命令提示符比缓冲区大小小得多,因此我什么也没收到,它只是坐在那里。所以 NSPipe也是不行的。

    some research之后,我确定需要使用 伪终端(pty)代替NSPipe。不幸的是,除了使它正常工作外,我什么都没有。

    我尝试过的

    代替stdout管道,我创建如下的pty:
    struct termios termp;
    bzero(&termp, sizeof(termp));
    int res = openpty(&masterFD, &slaveFD, NULL, &termp, NULL);

    这给了我两个文件描述符;我将 slaveFD移交给 NSFileHandle,然后将其传递给 NSTask,无论是stdout还是stdout和stdin都可以。然后,我尝试从主机端进行通常的异步读取。

    如果我在“终端”窗口中运行要控制的程序,则它首先输出两行文本,其中一行长18字节(包括换行符),一列22字节,命令提示符不包含换行符。在这40个字节之后,它等待输入。

    如果仅将pty用于stdout,则从受控程序接收到18个字节的输出(恰好一行,以换行符结尾),并且没有更多输出。最初的18个字节之后,所有内容都坐在那里,不再有其他事件-GCD事件源的处理程序不会被调用。

    如果我也将pty用于stdin,则通常会收到19个字节的输出(上述行加上下一行的一个字符),然后受控程序立即死亡。如果在尝试读取数据之前稍等片刻(或者调度噪音会导致小的暂停),则实际上我会获得全部40个字节,然后程序立即立即死亡。

    额外的死胡同

    在某一时刻,我想知道我的异步读取代码是否存在缺陷,因此我使用 NSFileHandle和它的 -readInBackgroundAndNotify方法重新进行了所有操作。这与使用GCD时的行为相同。 (我最初是通过 NSFileHandle API选择GCD的,因为 NSFileHandle中似乎没有任何异步写入支持)

    问题

    经过一天多的徒劳尝试,到了这一点,我可以提供一些帮助。我要执行的操作是否存在一些基本问题?为什么将stdin连接到pty会终止程序?我没有关闭pty的主节点,因此它不应该接收EOF。撇开标准输入,为什么我只能得到一行的输出值?我在pty的文件描述符上执行I/O的方式是否存在问题?我使用主控端和从属端是否正确-控制过程中的主控端,NSTask中的从属端?

    我没有尝试过

    到目前为止,我仅对管道和pty执行非阻塞(异步)I/O。我唯一能想到的就是pty根本不支持这一点。 (如果是这样,为什么 fcntl(fd, F_SETFL, O_NONBLOCK);为什么会成功?)我可以尝试在后台线程上阻止I/O,然后将消息发送到主线程。我希望避免处理多线程,但是考虑到所有这些API看起来多么破损,与尝试异步I/O的另一个排列相比,这不会浪费更多的时间。不过,我很想知道我到底在做什么错。

    最佳答案

    The problem is likely that the stdio library inside is buffering output. The output will only appear in the read pipe when the command-line program flushes it, either because it writes a "\n" via the stdio library, or fflush()s, or the buffer gets full, or exits (which causes the stdio library to automatically flush any output still buffered), or possibly some other conditions. If those printf strings were "\n"-terminated, then you MIGHT the output quicker. That's because there are three output buffering styles -- unbuffered, line-buffered (\n causes a flush), and block buffered (when the output buffer gets full, it's auto-flushed).

    Buffering of stdout is line-buffered by default if the output file descriptor is a tty (or pty); otherwise, block buffered. stderr is by default unbuffered. The setvbuf() function is used to change the buffering mode. These are all standard BSD UNIX (and maybe general UNIX) things I've described here.

    NSTask does not do any setting up of ttys/ptys for you. It wouldn't help in this case anyway since the printfs aren't printing out \n.

    Now, the problem is that the setvbuf() needs to be executed inside the command-line program. Unless (1) you have the source to the command-line program and can modify it and use that modified program, or (2) the command-line program has a feature that allows you to tell it to not buffer its output [ie, call setvbuf() itself], there's no way to change this, that I know of. The parent simply cannot affect the subprocess in this way, either to force flushing at certain points or change the stdio buffering behavior, unless the command-line utility has those features built into it (which would be rare).


    资料来源: Re: NSTask, NSPipe's and interactive UNIX command

    关于macos - 从Cocoa应用程序控制交互式命令行实用程序-PTYS的麻烦,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12586555/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com