Here 文件

here 文件，又稱作 heredoc、hereis、here - 字串或 here - 指令碼，是一種在命令列 shell（如 sh、csh、ksh、bash、PowerShell 和 zsh）和程式語言（像 Perl、PHP、Python 和 Ruby）裡定義一個字串的方法。它可以儲存文字裡面的換行或是縮排等空白字元。一些語言允許在字串裡執行變數替換和命令替換。

here 文件最通用的語法是緊跟一個識別符號，從下一行開始是想要引用的文字，然後再在單獨的一行用相同的識別符號關閉。在 Unix shell 裡，here 文件通常用於給命令提供輸入內容。

例項

以下幾節提供了不同語言和環境中的例子。

1.1 命令列 shell

1.1.1 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

1.1.2 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:\>

1.1.3 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!

1.2 程式語言

1.2.1 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";

1.2.2 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);

}

1.2.3 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=

2.1 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 $shellscriptstdout =

可以在同一行上開始多個 here 文件：

say (

標籤本身可以使用空格，這允許 here 文件不會破壞縮排。

say

2.2 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 一樣，和不用引號的效果一樣。

2.3 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 函式。

2.4 R

R 語言在字串裡使用空格，包括換行。不執行變數替換。字串可以用 textConnection 函式轉化為檔案描述符。例如，以下程式碼將一個嵌入原始碼的資料錶轉化為一個數據框架變數：

str

2.5 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

2.6 Ruby

下列 Ruby 程式碼用 here 文件顯示了一個列表：

puts

輸出：

Grocery list

------------

Salad mix.

Strawberries. 3. Cereal.

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!"

2.7 Tcl

Tcl 沒有為 here 文件設立特殊的語法，因為一般的字串語法已經允許嵌入換行和保持縮排。用括號括起來的字串，沒有擴充套件：

puts {

Grocery list

------------

Salad mix.

Strawberries.3. Cereal.

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

2.1 其它

2.1.1 微軟 NMAKE

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

target0: dependent0

command0