Batch 模式下如何打印进度条?

C-c 结束之前看不到任何输出:

emacs --batch --eval '(while t (princ ".") (sit-for 1))'

除非多打一个换行符:

emacs --batch --eval '(while t (princ ".\n") (sit-for 1))'

解决了,用 external-debugging-output :sweat_smile:

这个函数似乎只能写到stderr,如何写入stdout?

恐怕没有办法立即写到 stdout 。

大致看了一下,所有打印到 stdout 的函数最后都会调用 printchar:

static void
printchar (unsigned int ch, Lisp_Object fun)
{
  if (!NILP (fun) && !EQ (fun, Qt))
    ...
  else
    {
      ...
      if (NILP (fun))
        {
          ...
        }
      else if (noninteractive)
        {
          printchar_stdout_last = ch;
          if (DISP_TABLE_P (Vstandard_display_table))
            printchar_to_stream (ch, stdout);
          else
            fwrite (str, 1, len, stdout);
          noninteractive_need_newline = 1;
        }
      else
        {
          ...
        }
    }
}

一般情况下都是走 printchar_to_stream (ch, stdout); 这个分支,它跟 external-debugging-output 差别就是 stdout;如果把 standard-display-tablenil,让 fwrite (str, 1, len, stdout); 执行,结果也没有立即输出,还是需要换行符:

emacs --batch \
      --eval '(setq standard-display-table nil)' \
      --eval '(while t (princ ".") (sit-for 1))'

所以恐怕就算 external-debugging-output 允许指定 stdout 也不能用来打印进度条。

C -h f set-binary-mode (需要至少 Emacs 25.1)

As a side effect, this function flushes any pending STREAM’s data.

emacs --batch --eval \
'(while t (princ ".") (set-binary-mode \'stdout nil) (sit-for 1))'

另外 \r 会移动光标到行首,对写进度条有帮助

;; 打印 0% 到 99%
(dotimes (i 100)
  (princ (format "%2d%%\r" i))
  (set-binary-mode 'stdout nil)
  (sit-for .1))
3赞

赞。

不知道如果一直用 binary 模式、不恢复 text 模式会有什么影响。

想知道楼主在玩什么大工程?

并没有。只是一个简单的测试。

这里的 text / binary 模式应该指 Emacs 在 IO 时要不要处理编码,文本文件要编码,二进制文件(如图片)不考虑编码。set-binary-mode 似乎只能针对 stdin/stdout/stderr 这三个文件描述符(这个函数大致就是 setmode(3) 的包装),所以如果要从标准输入读取二进制输入,如图片,应该把 stdin 改到 binary 模式

cat foo.png | emacs --batch ...

如果后续还要读入文本,如 UTF-8 文本,应该改成 text 模式?当然不改自己把二进制数据解码成 UTF-8 应该也行?不太清楚。