here文档,又称作heredoc、hereis、here-字串或here-脚本,是一种在命令行shell(如sh、csh、ksh、bash、PowerShell和zsh)和程序语言(像Perl、PHP、Python和Ruby)裡定义一个字串的方法。它可以保存文字裡面的换行或是縮排等空白字元。一些语言允许在字串里执行变量替换和命令替换。
here文档最通用的语法是<<紧跟一个标识符,从下一行开始是想要引用的文字,然后再在单独的一行用相同的标识符关闭。在Unix shell里,here文档通常用于给命令提供输入内容。
实例
以下几节提供了不同语言和环境中的例子。
命令行 shell
Unix shell
在以下几个例子中,文字用here文档传递给tr命令。
$ tr a-z A-Z one two three
> uno dos tres
> END_TEXT
ONE TWO THREE
UNO DOS TRES
END_TEXT被用作标识符。它指定了here文档的开始和结束ONE TWO THREE和UNO DOS TRES是执行后tr的输出。
在<<后面添加一个减号,可以使TAB字元被忽略。这允许在shell脚本中缩进here文档而不改变它们的值。(注意在命令行上可能会需要输入Ctrl-v TAB来真正地输入一个制表符。下边的例子用空格模拟制表符;不要复制粘贴。)
$ tr a-z A-Z one two three
> uno dos tres
> END_TEXT
ONE TWO THREE
UNO DOS TRES
默认地,会进行变量替换和命令替换:
$ cat Working dir $PWD
> EOF
Working dir /home/user
这可以通过使用引号包裹标识符来禁用。可以使用单引号或双引号:
$ cat Working dir $PWD
> EOF
Working dir $PWD
bash,ksh或zsh中也可以用here-字串:
$ tr a-z A-Z
Windows 命令行
等价的代码目前没有找到。下列代码较为有用。
set GREETING=Hello
echo %GREETING%
cmd /k
echo %GREETING%
set GREETING=Goodbye
echo %GREETING%
exit
echo %GREETING%
C:\>
C:\>set GREETING=Hello
C:\>echo %GREETING%
Hello
C:\>cmd /k
C:\> echo %GREETING%
Hello
C:\> set GREETING=Goodbye
C:\> echo %GREETING%
Goodbye
C:\>exit
C:\>echo %GREETING%
Hello
C:\>
Windows PowerShell
在Windows PowerShell里,here文档表示的是here-字串。一个here-字串是由@"或@'开始,由独立成行的"@或'@结束的字串。所有在开始符号和结束符号之间的字符都被当做字面的字串。
使用双引号引起来的here-字串允许变量替换,而单引号不行。
变量替换只发生于简单变量(如$x,但不是$x.y或$x[0])。
可以将命令放进$()中来获取执行结果。
在如下的PowerShell的代码中,文字使用here-字串传递给一个函数。这个函数ConvertTo-UpperCase定义如下:
PS> function ConvertTo-UpperCase($string) { $string.ToUpper() }
PS> ConvertTo-UpperCase @'
>> one two three
>> eins zwei drei
>> '@
>>
ONE TWO THREE
EINS ZWEI DREI
下边是一个证明了双引号的here-字串里的变量替换和命令替换的例子:
$doc, $marty = 'Dr. Emmett Brown', 'Marty McFly'
$time = [DateTime]'Friday, October 25, 1985 8:00:00 AM'
$diff = New-TimeSpan -Minutes 25
@"
$doc : Are those my clocks I hear?
$marty : Yeah! Uh, it's $($time.Hour) o'clock!
$doc : Perfect! My experiment worked! They're all exactly $($diff.Minutes) minutes slow.
$marty : Wait a minute. Wait a minute. Doc... Are you telling me that it's $(($time + $diff).ToShortTimeString())?
$doc : Precisely.
$marty : Damn! I'm late for school!
"@
输出:
Dr. Emmett Brown : Are those my clocks I hear?
Marty McFly : Yeah! Uh, it's 8 o'clock!
Dr. Emmett Brown : Perfect! My experiment worked! They're all exactly 25 minutes slow.
Marty McFly : Wait a minute. Wait a minute. Doc... Are you telling me that it's 08:25?
Dr. Emmett Brown : Precisely.
Marty McFly : Damn! I'm late for school!
如果用单引号的here-字串代替,输出看起来会像这样:
$doc : Are those my clocks I hear?
$marty : Yeah! Uh, it's $($time.Hour) o'clock!
$doc : Perfect! My experiment worked! They're all exactly $($diff.Minutes) minutes slow.
$marty : Wait a minute. Wait a minute. Doc... Are you telling me that it's $(($time + $diff).ToShortTimeString())?
$doc : Precisely.
$marty : Damn! I'm late for school!
编程语言
C++
C++11引入了原始字面字串。原始字面字串的前缀有一个“R”,以"分隔符(开始,以)分隔符"结束。分隔符可以是0到16字符长,可以包括简单的字符,除开空格,括号与反斜杠。
char const *a = R"(The escape sequence '\n' represents a newline character.)";
wchar_t const *b = LR"...(Raw strings look like R"(...)")...";
char16_t const *b = uR"xyz(
Universal character names such as "\u5367\u864E\u85CF\u3863" are not
processed in raw string literals. Therefore the above can be written
as "臥虎藏龍" in a raw string literal, but only if the source character
set contains those characters.
)xyz";
D语言
从2.0版本开始,D语言支持用“q”引导的here-字串。这些字串以一个括号(<>,[],(),{})或者单独成行的标识符开始。
下列D代码展示了使用括号和标识符的here-字串。
int main() {
string list = q"[1. Item One
- Item Two
- Item Three]";
writef( list );
}
使用标识符:
int main() {
string list = q"IDENT
- Item One
- Item Two
- Item Three
IDENT";
writef( list );
}
Lua
Lua使用和定义字面字串,字面字串中的换行会原样保留,不允许含有转义字符。这不便放置长的注释(--注释)和一些字串(x = a[b[c]])。所以在版本5.1时,Lua添加了一个新语法:起始的两个括号中间可以加入任意多的等号,并且只有相同的等号数字才能关闭字串。
local ls =
Initial newline isn't part of the string.
Two lines.
local lls = [==[
This notation can be used for Windows paths:
local path = [=[C:\Windows\Fonts]=]
]==]
Perl
在Perl里有许多不同的方法使用here文档。在here文档的标签名前后加括号的效果和一般的字面字串效果是一样的:标签前后加双引号允许变量扩展,单引号则不行,不加引号的和加双引号的效果一样。加反引号将会把here文档当做shell脚本执行,并获取输出。需要保证结束标签必须在一行的开始,不然这个标签不会被直译器认出。
注意here文档不是从标签开始的,而是从下一行开始的。所以包含标签的语句将会在标签后继续。
这是一个使用双引号的例子:
my $sender = "Buffy the Vampire Slayer";
my $recipient = "Spike";
输出:
Dear Spike,
I wish you to leave Sunnydale and never return.
Not Quite Love,
Buffy the Vampire Slayer
这是使用单引号的例子:
输出:
Dear $recipient,
I wish you to leave Sunnydale and never return.
Not Quite Love,
$sender
另外一个使用反引号的例子(可能不具有可移植性):
my $shell_script_stdout =
可以在同一行上开始多个here文档:
say(
标签本身可以使用空格,这允许here文档不会破坏缩进。
say
PHP
输出:
This is a heredoc section.
For more information talk to Joe Smith, your local Programmer.
Thanks!
Hey Joe Smith! You can actually assign the heredoc section to a variable!
包含关闭标识符的行不得包含除了(可选的)分号的任何其他字符。不然它就不会被识别为关闭标识符,PHP就会继续寻找一个。如果没有找到关闭标识符,分析错误会发生在最后一行。
在PHP 5.3和以后的版本中,就像Perl一样,可以用单引号包裹标识符阻止变量扩展;这叫作nowdoc:
$x =
在PHP5.3和以后的版本中,也可以用双引号包裹标识符,像Perl一样,和不用引号的效果一样。
Python
Python支持使用三个连续单引号或双引号的字面字串(如**或""")。这些字面字串可以跨越多行,支持here文档的功能。
一个简单的Python3兼容的例子给出像上边第一个Perl例子一样:
message="""Dear {recipient},
I wish you to leave Sunnydale and never return.
Not Quite Love,
{sender}
"""
print(message.format(sender='Buffy the Vampire Slayer', recipient='Spike'))
在Python3.0以前的版本中,用print关键字代替print函数。
R
R语言在字串里使用空格,包括换行。不执行变量替换。字串可以用textConnection()函数转化为文件描述符。例如,以下代码将一个嵌入源码的数据表转化为一个数据框架变量:
str
Racket
Racket的here字串以#开始,紧跟定义字串终止的标识符。
字串的内容包括所有的在#一行和仅包括定义了的终止符的那一行。即:字串的内容开始于#后的新行,结束于终止符之前的一行。
#lang racket
(displayln
#
输出:
This is a simple here string in Racket.
- One
- Two
- Three
here字串中的转义字符不被识别;字串(和终止符)中所有的字符都会保持原样。
#lang racket
(displayln
#
输出:
This string spans for multiple lines
and can contain any Unicode symbol.
So things like λ, ☠, α, β, are all fine.
In the next line comes the terminator. It can contain any Unicode symbol as well, even spaces and smileys!
here字串可以像一般的字串一样使用:
#lang racket
(printf #
输出:
Dear Isaac,
Thanks for the insightful conversation yesterday.
Carl
一个有趣的替代方案是使用语言的扩展at-exp来写@-表达式。
它们看起来像这样:
#lang at-exp racket
(displayln @string-append{
This is a long string,
very convenient when a
long chunk of text is
needed.
No worries about escaping
"quotes". It's also okay
to have λ, γ, θ, ...
Embed code: @|(number->string (+ 3 4))|
})
输出:
This is a long string,
very convenient when a
long chunk of text is
needed.
No worries about escaping
"quotes". It's also okay
to have λ, γ, θ, ...
Embed code: 7
Ruby
下列Ruby代码用here文档显示了一个列表:
puts
输出:
Grocery list
------------
- Salad mix.
- Strawberries.*
- Cereal.
- Milk.*
- Organic
写入文件:
File::open("grocery-list", "w") do |f|
f
Ruby也允许标识符不起始于行首,需要以起始here文档。
另外,Ruby对待here文档就像一个双引号括起来的字串,即可以使用#{}来进行代码替换。
以下例子展示了这2个特性:
now = Time.now
puts
但是如果标识符是用单引号引起来的,则当做单引号内的字串对待。第一种方法创建一个临时文件。第二种创建(或覆盖)特定文件。所有的行内文件都终止于独自成行的<<,后边可以添加不区分大小写的KEEP或NOKEEP来决定该文件是否保留。两个都不添加和加入NOKEEP效果一样。
target0: dependent0
command0
参见
*tr (Unix)关于tr程序的说明。
*管道 (Unix)关于管道的信息。
参考
外部链接
- [http://rosettacode.org/wiki/Here_document Here document]。超过15种语言里的here文档。
评论 (0)