在本节中,我们将介绍如何创建一个 异步进程(asynchronous process)。异步进程创建后会与 Emacs 并行运行,Emacs 可以通过后续小节介绍的函数与其通信(see 向进程发送输入 和 see 接收进程输出)。注意进程通信只是部分异步:Emacs 仅在调用这些函数时才会与进程收发数据。
异步进程通过 pty(伪终端)或 管道(pipe) 进行控制。在创建进程时会选择 pty 或管道,默认基于变量 process-connection-type 的值(见下文)。如果可用,pty 通常更适合用户可见的进程(例如 Shell 模式),因为它们支持进程与子进程之间的任务控制(C-c、C-z 等),并且交互式程序会将 pty 视为终端设备,而管道不支持这些功能。但对于 Lisp 程序内部使用的子进程(即无需用户与子进程交互),如果需要在子进程与 Lisp 程序间交换大量数据,通常更适合使用管道,因为管道效率更高。此外,许多系统上 pty 的总数有限,避免不必要地浪费它们是更好的做法。
该函数是启动异步子进程的基础底层原语,返回表示该子进程的进程对象。与下文介绍的更高级别的 start-process 相比,它使用关键字参数,更灵活,可在单次调用中指定进程过滤器和哨兵函数。
参数 args 是一组关键字/参数对。省略某个关键字等价于将其值设为 nil。有效关键字如下:
:name name使用字符串 name 作为进程名;若同名进程已存在,会修改 name(追加 ‘<1>’ 等)以保证唯一。
:buffer buffer使用 buffer 作为进程缓冲区。若值为 nil,子进程不与任何缓冲区关联。
:command command使用 command 作为进程命令行。该值应为一个列表,首元素为程序可执行文件名,后续字符串为程序参数。若列表首元素为 nil,Emacs 会打开一个新的伪终端(pty)并将其输入输出关联到 buffer,不实际运行任何程序;此时列表其余元素会被忽略。
:coding coding若 coding 为符号,指定用于连接数据读写的编码系统。若 coding 为 cons 单元格 (decoding . encoding),则 decoding 用于读取,encoding 用于写入。用于编码写入程序数据的编码系统也会用于编码命令行参数(程序本身除外,其文件名编码与其他文件名一致;see file-name-coding-system)。
若 coding 为 nil,则使用编码系统的默认查找规则。See 默认编码系统。
:connection-type type初始化与子进程通信的设备类型。可选值:pty 使用伪终端,pipe 使用管道,nil 使用由变量 process-connection-type 决定的默认值。若 type 为 cons 单元格 (input . output),则 input 用于标准输入,output 用于标准输出(若 :stderr 为 nil,也用于标准错误)。
在不支持 pty 的系统(MS-Windows)上,该参数会被忽略,无条件使用管道。
:noquery query-flag初始化进程退出询问标志为 query-flag。See 退出前询问确认。
:stop stopped若提供该参数,stopped 必须为 nil,使用非空值会报错。:stop 关键字仅为兼容管道进程等其他进程类型而保留,会被忽略。异步子进程不会以停止状态启动。
:filter filter初始化进程过滤器为 filter。未指定时会使用默认过滤器,后续可覆盖。See 进程过滤器函数。
:sentinel sentinel初始化进程哨兵函数为 sentinel。未指定时会使用默认哨兵函数,后续可覆盖。See 哨兵函数:检测进程状态变化。
:stderr stderr将 stderr 与进程标准错误关联。非空值应为缓冲区或下文介绍的 make-pipe-process 创建的管道进程。若 stderr 为 nil,标准错误与标准输出合并,一同发送至 buffer 或过滤器。
若 stderr 为缓冲区,Emacs 会创建一个管道进程,即 标准错误进程(standard error process)。该进程使用默认过滤器(see 进程过滤器函数)、哨兵函数(see 哨兵函数:检测进程状态变化)和编码系统(see 默认编码系统),但使用 query-flag 作为退出询问标志(see 退出前询问确认)。它会与 stderr 缓冲区关联(see 进程缓冲区)并将输出(主进程的标准错误)写入其中。可将 stderr 缓冲区传入 get-buffer-process 获取标准错误进程的进程对象。
若 stderr 为管道进程,Emacs 会将其作为新进程的标准错误进程。
:file-handler file-handler若 file-handler 非空,查找当前缓冲区 default-directory 对应的文件名处理器,并调用该处理器创建进程。若无对应处理器,则按 file-handler 为 nil 处理。
可通过 process-contact 函数获取包含实际连接信息的原始参数列表。
子进程的当前工作目录设为当前缓冲区的 default-directory 值(本地目录时,由 unhandled-file-name-directory 判断),否则为 ~。若需要在远程目录运行进程,向 make-process 传入 :file-handler t。此时当前工作目录为 default-directory 的本地名称部分(由 file-local-name 决定)。
根据文件名处理器的实现,可能无法对生成的进程对象应用 filter 或 sentinel。:stderr 参数不能是管道进程,文件名处理器不支持此用法。:stderr 可使用缓冲区,其内容无需管道进程即可显示。See 进程过滤器函数、哨兵函数:检测进程状态变化 和 接收进程输出。
部分文件名处理器可能不支持 make-process,此时该函数无操作并返回 nil。
该函数创建可关联到子进程的双向管道,常用于配合 make-process 的 :stderr 关键字使用,返回进程对象。
参数 args 是一组关键字/参数对。省略某个关键字等价于将其值设为 nil。
有效关键字如下:
:name name使用字符串 name 作为进程名,与 make-process 相同,必要时会修改以保证唯一。
:buffer buffer使用 buffer 作为进程缓冲区。
:coding coding若 coding 为符号,指定用于连接数据读写的编码系统。若 coding 为 cons 单元格 (decoding . encoding),则 decoding 用于读取,encoding 用于写入。
若 coding 为 nil,则使用编码系统的默认查找规则。See 默认编码系统。
:noquery query-flag初始化进程退出询问标志为 query-flag。See 退出前询问确认。
:stop stopped若 stopped 非空,以停止状态启动进程。停止状态下管道进程不接收传入数据,但可发送传出数据。停止状态由 stop-process 设置,continue-process 清除(see 向进程发送信号)。
:filter filter初始化进程过滤器为 filter。未指定时会使用默认过滤器,后续可修改。See 进程过滤器函数。
:sentinel sentinel初始化进程哨兵函数为 sentinel。未指定时会使用默认哨兵函数,后续可修改。See 哨兵函数:检测进程状态变化。
可通过 process-contact 函数获取包含实际连接信息的原始参数列表。
该函数是 make-process 的高级封装,接口与 call-process 类似。它创建新的异步子进程并运行指定的 program,返回 Lisp 中代表该子进程的进程对象。参数 name 指定进程对象名称;与 make-process 相同,必要时会修改以保证唯一。buffer-or-name 为与进程关联的缓冲区。
若 program 为 nil,Emacs 打开新伪终端(pty)并将其输入输出关联到 buffer-or-name,不创建子进程,此时剩余参数 args 被忽略。
其余 args 为字符串,指定子进程的命令行参数。
在下例中,第一个进程启动并运行(实际为休眠)100 秒(输出缓冲区 ‘foo’ 会立即创建)。与此同时第二个进程启动,为保证唯一性命名为 ‘my-process<1>’。它在第一个进程结束前将目录列表插入缓冲区 ‘foo’ 末尾,随后结束并在缓冲区插入相应提示。很久之后第一个进程结束,同样在缓冲区插入提示信息。
(start-process "my-process" "foo" "sleep" "100")
⇒ #<process my-process>
(start-process "my-process" "foo" "ls" "-l" "/bin")
⇒ #<process my-process<1>>
---------- Buffer: foo ----------
total 8336
-rwxr-xr-x 1 root root 971384 Mar 30 10:14 bash
-rwxr-xr-x 1 root root 146920 Jul 5 2011 bsd-csh
...
-rwxr-xr-x 1 root root 696880 Feb 28 15:55 zsh4
Process my-process<1> finished
Process my-process finished
---------- Buffer: foo ----------
与 start-process 类似,该函数启动新异步子进程运行 program 并返回其进程对象。
与 start-process 的区别在于,该函数会根据 default-directory 的值调用文件名处理器。该处理器会运行 program,可能在本地主机,也可能在与 default-directory 对应的远程主机。后一种情况下,default-directory 的本地部分成为进程工作目录。
该函数不会为 program 或其余 args 调用文件名处理器。因此,若 program 或任意 args 使用远程文件语法(see 实现“魔法”文件名机制),必须通过 file-local-name 将其转换为相对于 default-directory 的文件名,或远程主机本地可识别的文件名。
根据文件名处理器的实现,可能无法对生成的进程对象应用 process-filter 或 process-sentinel。See 进程过滤器函数 和 哨兵函数:检测进程状态变化。
部分文件名处理器可能不支持 start-file-process(例如函数 ange-ftp-hook-function),此时该函数无操作并返回 nil。
该函数与 start-process 类似,区别在于使用 Shell 执行指定的 command。参数 command 为 Shell 命令字符串。变量 shell-file-name 指定所用 Shell。
通过 Shell 而非直接使用 make-process 或 start-process 运行程序,目的是可以在参数中使用通配符等 Shell 特性。因此,如果命令中包含任意用户指定参数,应先使用 shell-quote-argument 转义,避免特殊字符被 Shell 解析。See Shell 参数。当然,执行基于用户输入的命令时还应考虑安全问题。
该函数与 start-process-shell-command 类似,但内部使用 start-file-process。因此根据 default-directory,command 也可在远程主机执行。
该变量控制与异步子进程通信的设备类型。若非空,在可用时使用 pty,否则使用管道。
process-connection-type 的值在调用 make-process 或 start-process 时生效。因此可在调用这些函数时绑定该变量,指定与单个子进程的通信方式。
注意,若调用 make-process 时 :stderr 参数非空,该变量的值会被忽略;此时 Emacs 会使用管道与进程通信。在 pty 不可用的系统(MS-Windows)上同样会被忽略。
(let ((process-connection-type nil)) ; 使用管道
(start-process ...))
可使用 process-tty-name 函数判断指定子进程实际使用管道还是 pty(see 进程信息)。
若进程哨兵/过滤器函数发生错误,Emacs 默认会在显示错误后暂停 process-error-pause-time 秒,以便用户看到该错误。但这可能导致 Emacs 无响应(若大量此类错误发生),因此可将 process-error-pause-time 设为 0 禁用该行为。