首页
归档
友情链接
关于
Search
1
在wsl2中安装archlinux
296 阅读
2
从零开始配置 vim(15)——状态栏配置
174 阅读
3
nvim番外之将配置的插件管理器更新为lazy
163 阅读
4
2018总结与2019规划
151 阅读
5
PDF标准详解(五)——图形状态
127 阅读
软件与环境配置
读书笔记
编程
Thinking
FIRE
菜谱
翻译
登录
Search
标签搜索
c++
c
emacs
elisp
学习笔记
windows
文本操作术
编辑器
NeoVim
Vim
win32
读书笔记
VimScript
linux
文本编辑器
投资理财
Java
反汇编
OLEDB
数据库编程
Masimaro
累计撰写
460
篇文章
累计收到
33
条评论
首页
栏目
软件与环境配置
读书笔记
编程
Thinking
FIRE
菜谱
翻译
页面
归档
友情链接
关于
搜索到
1
篇与
的结果
2026-10-22
《elisp 每日一练》—— 同步进程调用
在前面我们了解了定时器的使用机制,但在日常使用中,通常是利用Emacs配合外部命名行工具的方式使用。所以我们今天讨论如何利用Emacs调用外部命令行工具基础知识我们今天主要讨论同步的方式调用,虽然它会阻塞Emacs的主线程,但是对于搜索、编译等工作来说,我们可能就是需要等待它们的执行结果。这种情况下使用同步的方式,反而简单一些。对于同步方式调用,Emacs提供了三个不同的接口。shell-command-to-stringprocess-linescall-processshell-command-to-string 的签名如下:(shell-command-to-string COMMAND)它通过系统的标准shell,执行 COMMAND 参数中输入的命令,并且等待结果,将结果以字符串的形式返回。当然我们也可以传入管道符号,对输出进行过滤,例如(shell-command-to-string "gcc -c main.c -o main | grep \\"error\\"")process-lines 与 shell-command-to-string 类似,但是它会自动将程序输出按行分隔为字符串列表返回(process-lines PROGRAM &rest ARGS)同时它也支持将程序的参数以多个字符串参数的形式输入。例如 (process-lines "git" "status" "--porcelain")call-process 是比较底层的函数,它不经过shell,直接创建一个新的进程,调用指定的二进制可执行程序,并将参数以列表形式逐个传入。它的签名如下:(call-process PROGRAM \&optional INFILE DESTINATION DISPLAY &rest ARGS)PROGRAM 是被调用的程序名称,如果是相对路径,Emacs 会在变量 exec-path 列出的目录中查找,需要注意的是这个参数应该只包含程序名,不能包含命令行参数INFILE 指定子进程的标准输入来源,如果为 nil 则从空设备(/dev/null) 中读取,否则应该是一个文件名,程序从指定文件中读取。DESTINATION 决定程序的输出,可以指定标准输出或者错误输出。DISPLAY 结果显示方式,如果是 nil,仅在函数完全结束后,结果才可见; 非 nil 会根据程序输出,即时刷新屏幕ARGS 是真正需要传递给子进程的参数DESTINATION 的取值可以有下面几个:缓冲区对象或名称,此时它会将输出打印到指定缓冲区光标所在位置的后面t 将输出插入到当前缓冲区中nil 丢弃所有输出0 丢弃输出并立即返回,不等待子进程结束。此时实际上是在异步执行,但是Emacs认为子进程已经执行完毕, Windows 上不支持(:file 文件名) 此时会将输出放入到一个指定文件中我们在上面说,它可以分别重定向标准输出和标准错误,它可以使用 (标准输出目标 标准错误目标) 这样的形式分别指定。各位读者如果对操作系统、子进程、shell等概念比较熟悉的话,应该很容易区分这些函数的使用场景。这里就不再深入讨论了。练习我们来实现一个简单的 git 信息显示的功能,以此来演示一下这些函数的用法。我们实现以下功能:通过 shell-command-to-string 调用 git branch --show-current 显示当前branch通过 process-lines 调用 git status --porcelain 获取当前修改的文件列表, 并显示到临时buffer中在用户选择上一步中某个文件之后,通过 call-process 调用 git diff, 查看差异(defun my/git-current-branch() (interactive) (let* ((output (shell-command-to-string "git branch --show-current")) (branch (string-trim output))) (if (string-empty-p branch) (message "当前目录可能不是一个git 仓库") (message "当前分支: %s" branch)))) (defun my/git-status() "在临时缓冲区中列出当前仓库修改的文件" (interactive) (let ((lines (process-lines "git" "status" "--porcelain")) (buf (get-buffer-create my/git-status-buffer-name))) (with-current-buffer buf (let ((inhibit-read-only t)) (erase-buffer) ;;插入提示信息 (insert (format "Branch: %s\n" (my/git-current-branch))) (insert (make-string 60 ?-) "\n") (if lines (dolist (line lines) (insert line "\n")) (goto-char (point-min))))) (pop-to-buffer buf))) (defun my/git-status--path-at-point () "从当前行解析出文件路径;若不是文件行则返回 nil。" (save-excursion (beginning-of-line) (let ((line (buffer-substring-no-properties (line-beginning-position) (line-end-position)))) ;; 跳过头部信息行 (when (and (>= (length line) 4) (not (string-prefix-p "Branch:" line)) (not (string-prefix-p "---" line))) (let ((path (substring line 3))) ; 去掉 "XY " 前缀 ;; 处理重命名 "OLD -> NEW" (when (string-match " -> " path) (setq path (substring path (match-end 0)))) ;; 处理含空格的路径(porcelain 会用引号包裹) (when (string-prefix-p "\"" path) (setq path (string-trim path "\"" "\""))) path))))) (defun my/git-show-diff-at-point() (interactive) (let ((path (my/git-status--path-at-point))) (if (not path) (message "当前行没有文件路径") (let ((diff-buff (get-buffer-create my/git-diff-buffer-name))) (with-current-buffer diff-buff (let ((inhibit-read-only t)) (erase-buffer) (insert (format "diff for: %s" path)) (insert (make-string 60 ?-) "\n") (call-process "git" nil t nil "diff" "HEAD" "--" path) ;; DESTINATION 设置为t,直接显示内容到当前buffer (goto-char (point-min)))) (display-buffer diff-buff)))))总结本节我们了解了Emacs中同步方式调用进程的几种方式,它们各有千秋:shell-command-to-stringprocess-linescall-process
2026年10月22日
0 阅读
0 评论
0 点赞