Shell Here Document(内嵌文档/嵌入文档)

Shell 还有一种特殊形式的重定向叫做“Here Document”,目前没有统一的翻译,你可以将它理解为“嵌入文档”“内嵌文档”“立即文档”。

所谓文档,就是命令需要处理的数据或者字符串;所谓嵌入,就是把数据和代码放在一起,而不是分开存放(比如将数据放在一个单独的文件中)。有时候命令需要处理的数据量很小,将它放在一个单独的文件中有点“大动干戈”,不如直接放在代码中来得方便。

Here Document 的基本用法为:

command <<END
    document
END

command是 Shell 命令,<<END是开始标志,END是结束标志,document是输入的文档(也就是一行一行的字符串)。

这种写法告诉 Shell 把 document 部分作为命令需要处理的数据,直到遇见终止符END为止(终止符END不会被读取)。

注意,终止符END必须独占一行,并且要定顶格写。

分界符(终止符)可以是任意的字符串,由用户自己定义,比如 END、MARKER 等。分界符可以出现在正常的数据流中,只要它不是顶格写的独立的一行,就不会被作为结束标志。

【实例1】cat 命令一般是从文件中读取内容,并将内容输出到显示器上,借助 Here Document,cat 命令可以从键盘上读取内容。
[mozhiyan@localhost ~]$ cat <<END
> Shell教程
> https://www.xinbaoku.com/shell/
> 已经进行了三次改版
> END
Shell教程
https://www.xinbaoku.com/shell/
已经进行了三次改版
<是第二层命令提示符。

正文中也可以出现结束标志END,只要它不是独立的一行,并且不顶格写,就没问题。
[mozhiyan@localhost ~]$ cat <<END
> END可以出现在行首
> 出现在行尾的END
> 出现在中间的END也是允许的
> END
END可以出现在行首
出现在行尾的END
出现在中间的END也是允许的

【实例2】在脚本文件中使用 Here Document,并将 document 中的内容转换为大写。
#!/bin/bash
#在脚本文件中使用立即文档

tr a-z A-Z <<END
one two three
Here Document
END
将代码保存到 test.sh 并运行,结果为:
ONE TWO THREE
HERE DOCUMENT

忽略命令替换

默认情况下,正文中出现的变量和命令也会被求值或运行,Shell 会先将它们替换以后再交给 command,请看下面的例子:
[mozhiyan@localhost ~]$ name=新宝库
[mozhiyan@localhost ~]$ url=https://www.xinbaoku.com
[mozhiyan@localhost ~]$ age=7
[mozhiyan@localhost ~]$ cat <<END
> ${name}已经${age}岁了,它的网址是 ${url}
> END
新宝库已经7岁了,它的网址是 https://www.xinbaoku.com

你可以将分界符用单引号或者双引号包围起来使 Shell 替换失效:
[mozhiyan@localhost ~]$ name=新宝库
[mozhiyan@localhost ~]$ url=https://www.xinbaoku.com
[mozhiyan@localhost ~]$ age=7
[mozhiyan@localhost ~]$ cat <<'END'  #使用单引号包围
> ${name}已经${age}岁了,它的网址是 ${url}
> END
${name}已经${age}岁了,它的网址是 ${url}

忽略制表符

默认情况下,行首的制表符也被当做正文的一部分。
#!/bin/bash

cat <<END
    Shell教程
    https://www.xinbaoku.com/shell/
    已经进行了三次改版
END
将代码保存到 test.sh 并运行,结果如下:
    Shell教程
    https://www.xinbaoku.com/shell/
    已经进行了三次改版

这里的制表符仅仅是为了格式对齐,我们并不希望它作为正文的一部分,为了达到这个目的,你可以在<<END之间增加-,请看下面的代码:
#!/bin/bash

#增加了减号-
cat <<-END
    Shell教程
    https://www.xinbaoku.com/shell/
    已经进行了三次改版
END
这次的运行结果为:
Shell教程
https://www.xinbaoku.com/shell/
已经进行了三次改版

总结

如果你尝试在脚本嵌入一小块多行数据,使用 Here Document 是很有用的,而嵌入很大的数据块是一个不好的习惯。你应该保持你的逻辑(你的代码)和你的输入(你的数据)分离,最好是在不同的文件中,除非是输入一个很小的数据集。

Here Document 最常用的功能还是向用户显示命令或者脚本的用法信息,例如类似下面的函数:
usage(){
    cat <<-END
        usage: command [-x] [-v] [-z] [file ...]
        A short explanation of the operation goes here.
        It might be a few lines long, but shouldn't be excessive.
END
}