Bash 函数用于将终端输出稳健地传输到 vim

Bash 函数用于将终端输出稳健地传输到 vim

我经常将终端输出重定向到 vim,采用这种方式,我厌倦了一直输入:

ls | vim -

我想定义一个函数v来为我执行此操作,即我希望能够输入:

v ls并且这以某种方式“扩展”到前一个命令。

我可以在简单的情况下用这种脚本来完成这项工作(当然,这可以重新分解为一个小函数):

#!/bin/bash 
 
touch crrt_cmd

while (( "$#" )); do 
  echo -n $1 >> crrt_cmd
  echo -n " " >> crrt_cmd
  shift 
done

chmod +x crrt_cmd
bash crrt_cmd | vim -

rm crrt_cmd

这对于非常简单的命令(例如ls或)很有效ls -l,但当存在某些管道时,它根本不起作用。

关于如何执行类似操作但又可以与任何有效的 bash 命令一起使用,您有什么想法吗?


因此,为了清楚起见,如果可能的话,我希望事情能够按照以下方式进行:

v ls | grep keyword | head -5

翻译过来就是:

ls | grep keyword | head -5 | vim -

答案1

工艺替代可能会提供最接近您想要的语法。

这很有效,而且可能符合你的喜好:

vim <(cmd1 | cmd2 | ...)

这样您就可以将整个命令的文本放在一起,并将其放在将其发送到的命令之后(而不是之前)vim,这似乎是您的主要目标。没有参数-是故意的。

您不需要为此定义函数或别名,但当然您可以通过定义为别名或函数vim来缩短:vv

alias v=vim
v() { vim "$@"; }

v <(cmd1 | cmd2 | ...)比 多了 一个字符v 'cmd1 | cmd2 | ...'。但它也避免了muru 警告. '"\可以出现在里面<( ),并且它们以通常的方式工作。这也可以很好地嵌套:您可以在命令中使用其他括号,只要它们的使用方式在语法上是正确的即可。

这是流程替代。Bash 创建一个可通过类似路径访问的管道/dev/fd/63(不要与命令本身所涉及的管道混淆)。它将管道的路径替换为<(cmd1 | cmd2 | ...),因此vim看到的文件名类似/dev/fd/63。您的命令cmd1 | cmd2 | ...在子 shell 中异步运行,其输出将发送到管道,管道将vim读取。(这就是为什么您不写-vim/dev/fd/63或最终调用的任何内容读取,而不是从标准输入读取。)

穆鲁指出您可以编写一个扩展为的别名vim <(

alias v='vim <('

您可以将此别名定义放在 中~/.bash_aliases或 末尾~/.bashrc

这使您更接近您最初想要的语法 - 您只需要)在命令末尾写入:

v cmd1 | cmd2 | cmd3)

进程替换还有一个额外的好处,就是您可以多次使用它,<( )在同一个命令中编写多个构造,以防您想分别运行多个命令并在单独的vim缓冲区中查看它们的输出。

vim <(cmd1 | cmd2 | cmd3) <(cmd4 <infile) <(FOO=bar cmd5) <(cmd6 | cmd7)

当然,正如muru所说,后缀操作仍然更简单对于运行管道并在中打开其输出的用例,因为您只需要在管道末尾vim添加另一个命令。vim

谢谢穆鲁为了洞察力可以v作为 的别名vim <(


最简单的情况:一种更好的方法来实现你的原始v

vim <( )如上所述,即使在最简单的情况下也可以使用。但是,如果您没有编写两个或更多命令的管道(cmd1 | cmd2)、对命令应用重定向(cmd <infile)或为命令的持续时间分配环境变量(FOO=bar cmd),那么它的语法就有点过度了。因此,您可能仍希望有一个函数可以执行原始v函数所做的事情。

您的实现运行一个循环来连接参数的文本。这很复杂,而且在大多数涉及引用的场景中也会中断,即使您修复了$1"$1"防止初始分裂通配符。在 中v 'foo bar',您的v函数(与任何函数一样)不会收到任何引号:它会看到foo bar。这很好,因为它将其作为单个参数接收……直到它构造一个包含它的脚本并运行该脚本,此时foo bar被解析为两个单词,它们成为两个参数。

幸运的是,有一种更可靠的方法,也更简单、更简短。"$@"扩展到传递给当前函数的所有参数,如果不在函数中,则扩展到当前脚本。单独的参数既不会进一步拆分也不会相互连接。所以你所需要的只是:

v() { "$@" | vim -; }

(当然,如果您定义v要做其他事情 - 也许是作为上面描述的别名vim <(- 那么您可能会想将其称为其他东西,也许u。)

运行该命令定义了一个 shell 函数v,该函数运行其第一个参数中指定的命令并传递其后续参数。这不会构造单独的脚本,也不会使用bash -ceval。因此,它不会冒错误拆分或连接参数的风险,因为参数永远不会被拆分或连接:它们在进入函数时是分开的,并"$@"单独使用它们。

您可以把它放在末尾,~/.bashrc这样它就会为您的交互式 shell 定义。

或者如果您希望它是一个脚本,那么请创建一个名为v(或任何您想要的命令名称)的文件,其内容如下:

#!/bin/bash
"$@" | vim -

将文件标记为可执行文件(chmod +x v),并将其放在列出的目录中$PATH。我建议,如果它存在,它将在您登录时~/bin自动添加到您的目录中(除非您已更改为不这样做)。$PATH~/.profile

答案2

函数不能对任意非简单的bash 中的命令,因为函数本身就是一个简单的命令 - 当函数定义开始发挥作用时,已经太晚了。别名的扩展可以包含管道,因此您可以在别名中添加管道,但当然只能在命令的开头添加:

$ alias e='echo foo |'
$ e grep bar  # expands to echo foo | grep bar

如果你选择疯狂,那必然会出现以下情况:

v () { eval "$@" | vim -; }
# v 'ls -l | nc' 

将要迟早都会引用地狱。

进行后缀操作,它更简单。


精神错乱的其他选择:

zsh 中的全局别名

您可以在 zsh 中的命令行的任何位置扩展别名(而不仅仅是开头):

% alias -g v='| vim -'
% echo foo | grep foo v
Vim: Reading from stdin...

相关内容