如何干净地拆分 shell 命令输出的 stdout 和 stderr 并将后者显示在 Vim 状态行中?

How to split stdout and stderr of shell command output cleanly and show the latter in Vim status line?

我使用 Vim 作为仪表板,monitor/update 许多 table 具有各种外部(过滤)命令,运行 来自宏。它们工作得非常好,除了错误输出(还不完美,虽然我已经解决了这个消息集成的 80%。请参阅 p.s。有关此类应用程序的故事)。

例如下面的table是针对"I love coffee"游戏的:

TYPE  | STAT |    NAME    | GOAL | DONE | TODO | S1 | S2 | S3
drink | .    | Latte      |  250 |  178 |   72 | .  | .  | .
drink | .    | Cocoa      |   99 |   46 |   50 | .  | 3  | .
drink | .    | Mocha      |  250 |  190 |   40 | 12 | 8  | .
drink | .    | Espresso   |   30 |    0 |   20 | 10 | .  | .
drink | .    | Iced Latte |   54 |    8 |   37 | .  | .  | 9
cake  | .    | Egg Tart   |   25 |   15 |    4 | 6  | .  | .
cake  | .    | Tiramisu   |   36 |    6 |   20 | 4  | 6  | .
cake  | .    | Brûlée     |   11 |    0 |    9 | .  | 2  | .
cake  | v    | Pudding    |   20 |   20 |    0 | .  | .  | .

我在寄存器 s 中有以下宏:

'oV}!order-update.pl 2>/tmp/vim.err ^M:if getfsize('/tmp/vim.err')>0|echo join(readfile('/tmp/vim.err'),"\|")|endif ^M'o

(^M是字符ctrl-M), 当我触及 Store1/2/3 中的数量时,这有助于更新 DONETODO 列。 (如果完成,将进行 v 检查并将线向下移动到历史区域的顶部。)

S1/S2/S3的数字有问题时,重定向2>会从stdout输出溢出stderr输出并保存在vim.err 文件。 (不要弄乱 table)

if...endif 块将返回错误输出并将其回显到状态行。当我将 9 放入 S3 时,它将打印 Store3 drink: 9 > 8 max。 (但是 S3 最多可以装 8 杯饮料。)

我的问题是最后一个 ex 命令行:

:if getfsize('/tmp/vim.err')>0|echo join(readfile('/tmp/vim.err'),"\|")|endif

如果没有错误发生,将保留在状态行中。

我该如何改进它?


p.s。

此类应用程序的故事

上面的(简化的)示例演示了我如何使用 Vim 作为快速电子表格,因为我每天都会更新许多 table。好处是 - 我不必离开我最喜欢的 Vim.

通过一些语法高亮显示,Vim 可以以我最喜欢的方式呈现那些 table。 (取决于这些值)

使用相关过滤器,每个 table 都可以根据值的依赖关系正确更新。

多年来,我为各种项目开发了许多 syntax/filter 对。最近我意识到我还没有将过滤器的错误消息集成到 Vim 的环境中。此类消息将随机合并到正常输出中并弄乱table。 (使用 Vim 的默认设置 shellredir=">%s 2>$1")

我的目的是收集过滤器的 stderr 消息并将它们放入 Vim 的状态行。

我的实验取得了一些成果:

  1. 宏中指定的“2>vim.err”可以否决默认的“2>&1”,将stderr拆分到临时文件中。

  2. readfile() 函数非常适合读取整个文件。

  3. 对于检测到的小错误,过滤器最好不要以非零值退出。这样的值将使 Vim 做更多的事情并引发 "Hit-enter" 提示。

在状态行中顺利地调整 stderr 消息是我的最后一块拼图。

将宏保存在寄存器中对于在单个编辑会话中完成的重复性任务很好。对于您更经常需要的东西,我会定义一个适当的映射。对于 Vim,"upgrade" 很简单:宏内容成为 :map 命令的右侧(| 成为 <Bar>^M 成为 <CR>),你将其放入 ~/.vimrc:

:nnoremap <Leader>s 'oV}!order-update.pl ...

如果你使用:nnoremap <silent>,键将不会被打印出来,你应该只会看到:echo的结果。


要实用地修复您的宏,只需在末尾添加一个 else|echo|endif

我认为 "else|echo" 不是始终清除状态行的好解决方案。它很丑而且没有针对您的宏进行优化。

从过滤程序的标准错误中查看您的 tmp 文件 vim.err 的内容。如果过滤器没有发现任何奇怪的东西,它应该不包含任何内容。它的空内容将帮助您清除 OK 状态下的状态行。我会将您的宏重写为(更简单和更清晰):

'oV}!order-update.pl 2>/tmp/vim.err ^M:echo join(readfile('/tmp/vim.err'),"\|") ^M'o

另外,我不同意Karkat先生的评论:"Macro kept in a register is only good for a single editing session"。事实上,宏保存在 .viminfo 中并且在会话之间可用。它们永远不会消失,除非你给它们分配空值。在我看来,宏与映射一样强大。而且,宏比映射更方便(更容易创建和维护)。至少你可以避免逃避映射的噩梦。只有当它足够成熟并且在全球范围内有用时,我才会将宏转录为映射。