bash 中有一个我经常使用的功能,按下C-x e
将在中打开当前命令提示符$EDITOR
,关闭缓冲区时将执行写入的命令。
有一个相关命令,fc
它从 bash 历史记录中获取输入。手动的表明它根本不使用标准输入。
我想要一个cmd
类似于这样的命令:echo "ls" | cmd
打开一个包含的缓冲区ls
,并在写入该缓冲区时执行给定的命令(就像上面的任何一个命令一样)。是否有具有此属性的现有命令?
答案1
这个技巧在我的 Debian 9 中有效:
echo "ls" | vipe | . /dev/stdin
vipe
允许您在 unix 管道中间运行编辑器并编辑程序之间传输的数据。它来自包moreutils
。它尊重EDITOR
变量。
至少存在两个问题:
上面的命令
.
在子 shell 中起作用。您可以将例如放在foo=bar
缓冲区中并执行它,但它不会影响您当前的 shell。即使我们制作cmd
(例如,作为 shell 函数).
它在 shell 解释中起作用cmd
,您仍会像使用它一样使用它echo "ls" | cmd
,因此整个cmd
不会在您当前的 shell 中起作用。如果这个问题对您来说是一个问题,请进行研究
shopt -o lastpipe
并set +m
试验它们。命令将看到它们的 stdin 来自管道。我的测试表明,
.
在解释任何内容之前会读取整个文件(在我们的例子中是流)。您要运行的命令不会消耗流的某些部分,因为当它们启动时,管道已被关闭vipe
。在这种情况下
.
是安全的,但bash
实际上并非如此(至少在我的 Debian 9 中 GNU Bash 4.4.12 是安全的)。比较以下两个:yes cat | head -n 100 | . /dev/stdin # cats die silently yes cat | head -n 100 | bash # the first cat catches later cats
因此,
.
命令不会消耗管道;这很好。但有些命令可能会发现关闭的管道不可接受。例如:$ echo "top" | vipe | . /dev/stdin # change nothing and exit the editor top: failed tty get
以下函数尝试解决该问题:
cmd() { </dev/tty . <(vipe); }
现在如果您调用
echo "top" | cmd
那么vipe
将从管道读取,但是您在缓冲区中接受的命令将用作/dev/tty
其标准输入。Note
vipe
本身(将其输入流保存到文件后)操纵其描述符,打开并生成使用( )原始 stdin 和 stdout 的/dev/tty
编辑器。仍然不存在同时读取的风险,因为编辑器必须在将任何内容发送到其(原始)stdout 之前退出。该工具是一个 Perl 脚本,请阅读并亲自查看。/dev/tty
vipe
/dev/tty
vipe
也许您的用例不一定需要管道。也许您可以放入.
当前 shell。示例:
# initial commands taken from a file (template)
. <(</the/file vipe)
# initial commands generated by some program
. <(code_generator | vipe)
# initial commands provided as here document
. <(vipe <<EOF
ls
foo=baz
EOF
)
这些代码片段不仅会影响当前 shell(例如 set foo
),而且不会受到重定向输入的影响(例如top
不会出现任何抱怨)。
前两个代码片段可以转换为函数。您可以提供一个路径(分别指向模板或生成器,或者可能是整个 shell 命令)作为参数。基于第一个代码片段的示例函数:
template() { . <(<"$1" vipe); }
现在,您可以使用自定义修改重新获取您的~/.bashrc
(严格来说:它的副本)而无需实际修改原始文件。就像这样:
template ~/.bashrc
注意vipe
,除非您终止它或编辑器报告错误(即非零退出状态),否则将把临时文件打印到其标准输出。退出编辑器而不保存文件不足以中止;在这种情况下,原始文件将被进一步传输。如果您不想执行任何操作,请从文件中删除每个命令并保存它,这样它在获取源代码时就不会运行任何内容;或者让您的编辑器报告失败(例如:cq
在vi
)。