将Vim缓冲区传输到标准输出流

25

我想在管道中使用Vim。这篇现有的文章看起来就像是我想要做的,但我希望不依赖Python的帮助,仅使用bash实现。如果可以的话,环境是Android上Terminal IDE应用程序中的bash shell。

请注意,我知道如何从Vim 内部将缓冲区通过命令传递给另一个进程。那很好,但这不是我想要的。我想退出Vim,并将活动缓冲区传递到标准输出(stdout)。

顺便说一下,我也知道如何将另一个命令作为输入传递给Vim。同样,这不是我想要实现的目标。


可能是重复的问题:vim - 将缓冲区内容写入标准输出 - Ciro Santilli OurBigBook.com
可能的重复是你实际想要做的。你想先启动vim,然后将一些内容读入缓冲区,然后执行某些操作,然后将其输出以执行其他操作。不需要添加另一个花哨的shell工具(vipe)。 - erikbstack
5个回答

38

看一下moreutils中的vipe。它允许您将任何编辑器作为管道的一部分使用。

 ls -al | vipe | less

如果你想在 vim 中使用它,只需确保将其设置为你的默认编辑器在你的 bashrc 或者 cshrc 中或任何你使用的 shell 中。

 EDITOR=vim

更新:如果您想要一个仅使用bash的解决方案,您可以使用像这样的脚本

 #!/bin/bash
 # create temporary file
 TMPFILE=`mktemp /tmp/vipe.bashXXXXXXXX`
 cat > ${TMPFILE}
 vim ${TMPFILE} < /dev/tty > /dev/tty
 cat ${TMPFILE}
 rm ${TMPFILE}

为了更便携的版本,请替换

 vim ${TMPFILE}

使用

 ${EDITOR} ${TMPFILE}

当我将管道传输到 sed -e 's/^/XXX:/' 时,你的仅限于Bash的解决方案对我不起作用;我在跳转时会得到一个混乱的Vim编辑器。 - Ingo Karkat
太棒了。虽然我还没有完全搞定,但我认为这只是与终端IDE环境有关的路径问题。(我复制了你提供的bash脚本。) - Mike
1
我使它工作了,用这行代码替换: vim ${TMPFILE}vim ${TMPFILE} < /dev/tty > /dev/tty - Sebastián Grignoli
太棒了,谢谢!@SebastiánGrignoli,为什么它有效并且它是做什么的? - CervEd
@SebastiánGrignoli 这真的很好。对我来说运行得很好,除非没有标准输入(stdin)。有没有一种方法可以修改这个脚本,使其在没有stdin时也能工作?就像“vipe”之前没有命令一样。明显的解决方法是在vice之前添加echo''|,但肯定有一种方法可以让脚本处理这种情况。 - Mig

6
要将缓冲区打印到shell标准输出,vim需要在Ex模式下启动,否则它将以“正常”方式打开,并在退出时清除任何输出缓冲区。
以下是最简单的工作示例:
$ echo foo | vim +%p -escq /dev/stdin
foo

这与以下代码等效:

$ echo foo | vim -es '+%print' '+:q!' /dev/stdin
foo

为了防止额外的烦人消息,需要指定标准输入的特殊文件描述符(/dev/stdin)。

以下是一些解析字符串的示例:

$ echo This is example. | vim '+s/example/test/g' '+%p' -escq! /dev/stdin
This is test.
$ echo This is example. | vim - '+s/example/test/g' '+%p' -escq!
Vim: Reading from stdin...
This is test.

相关链接:


不幸的是,这似乎无法在nvim上工作,也无法在最新的vim 8(在macOS上)上工作。 - Konrad Rudolph
@KonradRudolph,我认为Neo Vim故意从其代码库中删除了Ex模式(称其是不受欢迎和无用的功能),因此它不会起作用。但我可能是错的。否则,如果还在那里,它可能以不同的方式工作。 - kenorb
我对这个问题的状态毫无头绪,但是 Ex 模式在 NeoVim 中肯定存在。无论如何,我已经进行了进一步的测试,发现这个解决方案在 Linux 上的 Vim 8 中也不起作用。请参见我的后续问题:https://unix.stackexchange.com/q/526036/3651 - Konrad Rudolph
1
其实不用管那个问题了。你在 Vim.SE 上的 ex 解决方案(https://vi.stackexchange.com/a/789/10024)可行。具体来说,就是这个命令:`ex -s "$@" '+%p|q!' /dev/stdin`。 - Konrad Rudolph

5

你不能简单地将vim放在管道中,因为这样Vim无法显示其用户界面。

ls | vim - | more # Does not work

解决这个问题的一种方法是在管道中使用gvim -f -,它会在单独的窗口中打开文件。您需要通过:w >> /dev/stdout写入文件,然后使用:quit!退出。

或者(在仅限控制台的非图形化环境下唯一的方法),您可以编写自己的脚本/函数vimAndMore,将应跟随vim的命令作为参数传递,并执行以下操作:

vimAndMore()
{
    TMPFILE=/tmp/pipecontents

    # Slurp stdin into the temp file.
    cat - > "$TMPFILE" || exit $?

    # Reconnect stdin to the terminal, so that Vim doesn't complain with "Warning:
    # Input is not from a terminal", and the terminal is kept intact.
    exec 0</dev/tty

    # Launch the editor.
    "${EDITOR:-vim}" "$TMPFILE" || exit $?

    # Carry on with the pipe.
    cat "$TMPFILE" | exec "$@"

    rm "$TMPFILE"
}

把管道替换为以下内容:

ls | vimAndMore more

如果有多个管道步骤需要执行,您必须使用显式的shell调用:

ls | vimAndMore sh -c 'tr a-z A-Z | more'

这在我的Ubuntu 22.04上无法工作:https://asciinema.org/a/539855。Vim读取输入,打印“Vim:警告:输出不是终端”,然后我必须盲目地导航/操作。最终按下`:wq`后,Vim退出并激活下一个管道。 - saulius2
另外,一种类似的逻辑对我也起作用():https://dev59.com/nWgv5IYBdhLWcg3wTvPL#10686830。它只是使用 vim“$TMPFILE”< /dev/tty > /dev/tty 而没有任何 execs。我猜你的解决方案只是缺少一行或两行重定向代码。但我仍然不确定具体是什么。干杯:) - saulius2
PS. 这里我在讨论另一种仅限控制台的方式。 - saulius2
1
@saulius2 最后一次调用中有一个错别字;正如评论所提到的,命令必须作为参数传递,而不是管道传递。我发布了这个解决方案,因为现在被接受的答案对我没有用,但与此同时,它已经被修复,所以我建议你使用它,因为它更不麻烦。 - Ingo Karkat

2

根据我的经验,Vim不支持向stdout输出内容。也不容易将Vim放入管道中。

你可以尝试使用这个软件包中的vipe命令。


1
> ls -1 fred*.c | vim -

执行该命令将导致Vim打开一个未命名的文件,其中包含文件列表fred*

也许我误解了你的问题...

另一种解释:

请参阅页面,其中描述了一种技术

在不保存当前缓冲区的情况下,您希望将其内容发送到解释器(python、scheme或其他),并且您希望该解释器在新生成的xterm中运行。新的xterm窗口很有用,因为在Vim内运行shell不允许同时检查程序输出和代码缓冲区,并且Vim的shell在几个方面都很弱。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接