之前也发过帖子, 但是没有发现问题的本质, 最近研究了下,发现问题的关键了,
process-coding-system-alist 这个变量大家肯定熟悉,比如可以这样设置某个进程输入输出的编码
(add-to-list 'process-coding-system-alist '("grep" . (utf-8-unix . utf-8-unix)))
但是在windows下会发现没效果
在windows下执行shell类的命令(shell / shell-command / async-shell-command)
他实际上是启动的cmdproxy去执行指定的命令,
而问题的关键就在于 process-coding-system-alist 只会作用于emacs的直接子进程
也就是说除非是用start-process直接启动grep, 否则设置了process-coding-system-alist也没有效果.
矛盾的地方就出现了
1 执行shell的时候默认是调用了cmdproxy -i , 他启动了一个cmd.exe, 是gbk编码
2 通过shell执行其他程序的时候, 特别是现代工具, 输出的都是utf-8编码的文本
3 执行任何shell命令,子进程都是cmdproxy, 用process-coding-system-alist只能设置一个编码
4 如果把cmdproxy设置成gbk,那2里面的程序输出就乱码, 如果设置成utf-8,那1中启动的cmd就乱码
其实linux下也是一样的,只是因为系统统一都是utf-8,所以对这个问题没感知
虽然可以在启动cmd的时候执行chcp,或者直接改系统的编码,但我觉得都不太完美.
最后想了下, 这样配置比较好
1 先统一所有编码为utf-8
2 针对涉及到gbk编码输出的地方做定制处理
(prefer-coding-system 'utf-8-unix)
(defun my-windows-builtin-command-p (cmd)
"Return non-nil if CMD is a native Windows CMD built-in command."
(let* ((builtin-commands
'("assoc" "break" "call" "cd" "chcp" "cls" "color" "copy" "date"
"del" "dir" "echo" "erase" "md" "mkdir" "move" "path" "pause"
"ren" "rename" "rd" "rmdir" "set" "start" "time" "type"
"ver" "vol"))
;; 提取命令的第一个单词作为真正的 cmd
(first (car (split-string cmd "[ \t]+" t))))
(member (downcase first) builtin-commands)))
(defun my-process-smart-encoding (orig-fun &rest args)
"Smartly choose GBK or UTF-8 for process based on the command name."
(let* ((cmd (car args))
(use-gbk (and (eq system-type 'windows-nt)
(my-windows-builtin-command-p cmd)))
(default-process-coding-system (if use-gbk '(gbk . gbk) '(utf-8 . utf-8)))
(locale-coding-system (if use-gbk 'gbk 'utf-8)))
(apply orig-fun args)))
(advice-add 'shell-command :around #'my-process-smart-encoding)
(advice-add 'async-shell-command :around #'my-process-smart-encoding)
这样配置后, 在以下场景都能正确显示中文
1 shell
2 shell-command
3 shell-command-to-string
4 async-shell-command
5 compile里面执行命令的输出
6 eshell
没解决的地方在于如果在shell里面执行输出utf-8的程序还是会有问题(有办法解决吗? 大召唤术 @include-yy ), 虽然用eshell能代替
更新:
看最终解决方案的代码,以下场景都正常了
1 shell(不管是执行内置的dir还是自己写的输出utf-8的程序)
2 shell-command
3 shell-command-to-string
4 async-shell-command
5 compile里面执行命令的输出
6 eshell
