网站搜索

这个 Bash 重定向技巧用来替换 echo ... | command


Bash——以及其他 Linux Shell——支持使用重定向等功能进行强大的命令连接。你可能已经习惯了最基本类型的输入重定向,但正如常见情况一样,Linux 比表面看起来的更复杂。

使用 < 进行输入重定向

输入重定向是你可能会学到的第一个高级 shell 技巧之一。它是一种机制,允许你将数据从文件发送到命令,而不是直接输入或通过另一个进程传输数据。

大多数程序——例如 cat——如果有意义的话,会接受作为命令行参数传入的文件名:

cat filename

但其他工具——例如 tr——则不是。根据其手册页,"tr 工具将标准输入复制到标准输出,同时对选定字符进行替换或删除。" 它的基本用法如下:

tr string1 string2

所以使用 tr 有两种典型方式:一种是在命令行上手动输入,另一种更常见的方式(尤其是在脚本中)是将输入通过管道传给它:

cat filename | tr string1 string2

这个管道使用 cat 将文件的内容发送到标准输出,但有一种更好的方法,那就是使用输入重定向:

tr string1 string2 <filename

这里,小于号符号使用文件描述符 0(标准输入)打开命名文件,使其可供 tr 使用。

带有 << 的 Here 文档

如果你曾经做过一些 shell 脚本编程,或者在命令行上处理输入,你可能遇到过一个常见问题:如何处理多行字符串。虽然你可以通过转义序列和其他方法来应付,但 here 文档语法要方便得多:

#!/bin/bash
cat <<END
This script will carry out an audit of your disk.
Choose an option:
1. Continue
2. Exit
END

在小于符号后面的单词(在本例中为“END”)是一个分隔符,你应该在最后一行单独使用它,以终止整个字符串。

这看起来可能像是一个完全不同的操作——将多行字符串格式化为单行——但 heredocs 是一种输入重定向形式。请注意,这个示例使用的是 cat,而不是 echo。虽然 echo 会打印你传给它的任何参数,但 cat 会打印标准输入,这正是我们在这里处理的内容。

你可以通过将 heredoc 传递给 echo 来确认这一点:

你的 shell 会使用不同的提示符来显示它正在等待多行输入。Bash 显示“> ”,而 zsh,如上图所示,使用“heredoc> ”。

echo 程序不会对标准输入做任何处理,所以在这种情况下不会发生任何事情。如果你运行等效的管道,也会看到同样没有输出的情况:

echo "hello, world" | echo

与此同时,cat 程序会将输入传递到标准输出:

神秘的 <<< 和这里的字符串

即使你是 heredoc 大师,你可能也不知道最后一种输入重定向:here 字符串。它实际上与多行版本非常相似,只是只用一行:

cat <<< "hello, world"

猫会失败,因为这相当于 echo hello, | cat world。如果你的 here 字符串包含空格,请确保使用引号。

因为这种语法期望的是单行字符串,所以不需要分隔符。像 here 文档一样,here 字符串可以执行变量替换,因此它的用途可以根据你的需要而非常灵活:

grep -q /usr/bin <<< $PATH

那么,这有什么用呢?首先,这种方法比其等效方法更稳健高效一些:

echo "hello, world" | echo

当一个命令被管道传输时,你的 shell 会打开一个子 shell 来运行它,并在两者之间适当地连接输入和输出。这在大多数情况下是一个非常宝贵的过程,但对于像将文本从一个命令传递到另一个命令这样简单的情况,它并不总是必要的。使用 here 字符串可以避免这种开销。

here字符串也可以作为更好的文档,更准确地反映命令的意图。与其滥用像echo或cat这样的命令来提供输入,不如让here字符串独立完成这个任务,并且它只做这件事,因此目的很明确。

因此,它作为占位符也很方便。例如,这个命令会在文本中搜索给定的模式:

grep "pattern" <<< "some text to search"

在构建模式时,你可能想使用虚拟文本来测试,这里的字符串可以让你轻松做到这一点。然后,当你完成了模式的优化后,你可以用从文件输入的重定向来替代它:

grep "pattern" < filename

整体命令几乎相同,因此保持了一致性,最终的命令也更容易制定。

不过,有时候,这里的字符串不仅仅是一个美化,它是必需的。以内置的 read 为例:

echo 'hello' | read greeting

这里的意图是让 $greeting 最终的值为 hello,但事实并非如此:

因为 read 在子 shell 中运行,所以 greeting 变量在调用的 shell 中不可用,因此输入会被丢弃。但是,使用 here string,一切都会按你想要的方式工作:

相关文章