Here文檔

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的輸出。
在
$ 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或$x0）。
可以將命令放進$()中來獲取執行結果。
在如下的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
2. Item Two
3. Item Three";
writef( list );
}
使用標識符：
int main() {
string list = q"IDENT
1. Item One
2. Item Two
3. Item Three
IDENT";
writef( list );
}

**Lua**
Lua使用和定義字面字串，字面字串中的換行會原樣保留，不允許含有轉義字符。這不便放置長的註釋（）和一些字串（x = abc）。所以在版本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";
print 
輸出：
Dear Spike,

I wish you to leave Sunnydale and never return.

Not Quite Love,
Buffy the Vampire Slayer
這是使用單引號的例子：
print 
輸出：
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
------------
1. Salad mix.
2. Strawberries. 3. Cereal.
4. Milk. - Organic
寫入文件：
File::open("grocery-list", "w") do |f|
f 
Ruby也允許標識符不起始於行首，需要以起始here文檔。
另外，Ruby對待here文檔就像一個雙引號括起來的字串，即可以使用#{}來進行代碼替換。
以下例子展示了這2個特性：
now = Time.now
puts 
但是如果標識符是用單引號引起來的，則當做單引號內的字串對待。
類似於Perl，Ruby允許在一行內開始多個here文檔：
puts \n" + \nAnd now it is over!"

**Tcl**
Tcl沒有為here文檔設立特殊的語法，因為一般的字串語法已經允許嵌入換行和保持縮進。用括號括起來的字串，沒有擴展：
puts {
Grocery list
------------
1. Salad mix.
2. Strawberries.3. Cereal.
4. Milk. - Organic
}
用引號括起來的字串在運行時執行替換：
set sender "Buffy the Vampire Slayer"
set recipient "Spike"
puts "
Dear $recipient, 
I wish you to leave Sunnydale and never return.
Not Quite Love,
$sender
"
在括號包裹的字串裡，起始括號和終止括號數量應該一樣多。在引號包裹的字串裡，括號可以不一樣多，但是反斜槓，美元符號和括號都會被替換，此時第一個沒有被轉義的雙引號會結束字串。
需要注意的一點是：上邊的字串的第一個和最後一個字符都是換行。string trim可以用來刪除頭尾空行：
puts string trim "
Dear $recipient, 
I wish you to leave Sunnydale and never return.
Not Quite Love,
$sender
" \n

**其它**

**微軟 NMAKE**
在微軟NMAKE裡，here文檔是行內的文件。行內文件以或開始。第一種方法創建一個臨時文件。第二種創建（或覆蓋）特定文件。所有的行內文件都終止於獨自成行的，後邊可以添加不區分大小寫的KEEP或NOKEEP來決定該文件是否保留。兩個都不添加和加入NOKEEP效果一樣。
target0: dependent0
command0 

**參見**
tr (Unix)關於tr程序的說明。
*管道 (Unix)關於管道的信息。

**參考**

**外部鏈接**
  Here documentdate=20200629092809 。超過15種語言裡的here文檔。