我经常将终端输出重定向到 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
来缩短:v
v
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
或最终调用的任何内容读取,而不是从标准输入读取。)
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 <(
)
如上所述,即使在最简单的情况下也可以使用。但是,如果您没有编写两个或更多命令的管道(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 -c
或eval
。因此,它不会冒错误拆分或连接参数的风险,因为参数永远不会被拆分或连接:它们在进入函数时是分开的,并"$@"
单独使用它们。
您可以把它放在末尾,~/.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...