ヒアドキュメント

ヒアドキュメントは、文字列リテラルを、シェルスクリプトやプログラミング言語のソースコード中に埋め込むための1つの方法である。

1. 概要
-----

ヒアドキュメントが構文に存在する言語として、sh、csh、ksh、Bash、zshなどUnixシェルのシェルスクリプトやPerl、PHP、Python、Rubyといったスクリプト言語があるが、これらに限られるものではない。改行やホワイトスペースなどが、書いたとおりに適用される。言語によっては、ヒアドキュメント内で変数展開やコマンド呼び出しを行える場合もある。

ヒアドキュメントのいちばん一般的な記法としては、<<の後に区切り文字となる識別子を続け、その次の行から対象となる文字列を書き、そして最初に指定した識別子だけの行で終わりとなる。Unixシェルでは、コマンドの入力を与えるのに使われることが多い。

2. 言語ごとの実装
----------

以下の節では、言語・環境ごとに実装の詳細を記述する。記法としては、Unixシェルと似たものが多いが、環境によっては別な構文で同等の機能を果たすことができたり、同様の構文を別な名前で呼んでいたりする例がある。また、一部の例ではシンタックスハイライトが正しく効いていないことがあるので、注意が必要である。

2.1. コマンドラインシェル
---------------

2.1.1. UNIXシェル
--------------

ヒアドキュメントは標準入力として扱われるため、標準入力を扱えるコマンドと組み合わせて使用する必要がある。文字列リテラルやファイルしか受け取らないようなコマンドで使用する場合は、変数を介して渡したり、bash固有の機能のProcess Substitutionを利用したりする必要がある。

以下の例では、ヒアドキュメントのテキストがtrコマンドへの入力となっている。

$ tr a-z A-Z < one two three > uno dos tres > END_TEXT ONE TWO THREE UNO DOS TRES ここではEND_TEXTを区切り文字としてあり、ヒアドキュメントの始めと終わりの位置を規定している。下2行のONE TWO THREEとUNO DOS TRESは、trコマンドを実行して出力された結果である。

ヒアドキュメントを開始するところで<<-とすると、行頭のタブが無視されるようになり、シェルスクリプトのインデントを崩さずにヒアドキュメントを書けるようになる。

$ tr a-z A-Z <<-END_TEXT > one two three > uno dos tres > END_TEXT ONE TWO THREE UNO DOS TRES

デフォルトで、変数展開やバッククオートによるコマンド展開も行われる。

$ cat << EOF > Working dir $PWD > EOF Working dir /home/user

これらは、区切り文字を引用符で囲むことで無効にできる。

$ cat << "EOF" > Working dir $PWD > EOF Working dir $PWD

また、bash、ksh、zshではヒアストリングという機能も存在する。

$ tr a-z A-Z <<<"Yes it is a string" YES IT IS A STRING

2.1.2. Windows PowerShell
-------------------------

Windows PowerShellでは、同様な機能がヒア文字列と呼ばれている。ヒア文字列は開始識別子の@"あるいは@'で始まり、対応する"@または'@だけの行で終わりとなり、間に入ったものすべてが文字列に含まれる。@"～"@のヒア文字列では変数展開が有効となるが、@'～'@では変数展開はなされない。ヒア文字列内の変数展開は、$xのような単純な変数、あるいは$という形の式が対象となる。

以下のPowerShellコードでは、ヒア文字列が関数への引数となっている。 function ConvertTo-UpperCase { $string.ToUpper }

ConvertTo-UpperCase @' one two three eins zwei drei '@<WtXmlEndTag /> 出力は以下のとおりである。

ONE TWO THREE EINS ZWEI DREI

次の例では、二重引用符で囲まれたヒア文字列内の変数展開やコマンドの実行が行われている。 $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 $ o'clock! $doc : Perfect! My experiment worked! They're all exactly $ minutes slow. $marty : Wait a minute. Wait a minute. Doc... Are you telling me that it's $.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!

一重引用符でヒア文字列を書いた場合、出力は以下のようになる。

$doc : Are those my clocks I hear? $marty : Yeah! Uh, it's $ o'clock! $doc : Perfect! My experiment worked! They're all exactly $ minutes slow. $marty : Wait a minute. Wait a minute. Doc... Are you telling me that it's $.ToShortTimeString)? $doc : Precisely. $marty : Damn! I'm late for school!

2.2. プログラミング言語
--------------

2.2.1. C#
---------

C#では逐語的文字列リテラル  と呼ばれる。アットマーク@をプレフィックスとする。正規表現のパターン文字列の記述などにも便利である。

using System.Text.RegularExpressions; ... string s = @"1. Item One 2. Item Two 3. Item Three"; System.Console.WriteLine; string path1 = "C:\\temp\\test.txt"; string path2 = @"C:\temp\test.txt"; System.Console.WriteLine; System.Console.WriteLine; System.Console.WriteLine; System.Console.WriteLine; // 逐語的文字列内にダブルクォーテーションを含める場合。 System.Console.WriteLine$").Success); System.Console.WriteLine$").Success);

2.2.2. C++
----------

C++11では、Raw文字列リテラルという機能が導入されている。Raw文字列リテラルは、Rをプレフィックスとし、続く"識別子識別子"の間に書く。識別子としては、0文字から16文字の範囲内で、 空白文字、括弧、バックスラッシュ以外の文字ならソースコードに書ける任意の文字を使って付けることができる。

// Raw文字列内のエスケープシーケンスは展開されない。 char const *a = R"";

// 識別子を使えば、Raw文字列の中に同様な表現を入れることができる。 wchar_t const *b = LR"..."のように書く)...";

char16_t const *b = uR"xyzxyz";<WtXmlEndTag />

2.2.3. D
--------

D言語では、バージョン2.0以降で、qをプレフィックスとした文字列がサポートされている。これらの文字列では、括弧 、<>、{}、[]のいずれか)あるいは改行の直後に識別子を置いて終端を示す。

括弧で囲まれるものは、このようになる。 int main { string list = q"[1. Item One 2. Item Two 3. Item Three]"; writef; }

識別子によるものは、このようになる。 int main { string list = q"IDENT 1. Item One 2. Item Two 3. Item Three IDENT"; writef; }

2.2.4. Lua
----------

Luaでは、文字列を[[と]]で挟んで文字列リテラルとすることができ、この間では改行もそのまま適用されるほか、エスケープ文字も認識されない。この文字列に他の目的で]]を含ませられないという問題があったので、 Lua 5.1では、開始地点となる2つの[の間に等号をいくつか入れて、ちょうど同じ数だけ等号を挟んだ]まで終わらせない、ということができるようになった。

local ls = [[ 最初の改行は文字列の一部とはならない。 つまり、この文字列は2行である。]]

-- 等号を使った記法 local lls = [==[ この記法は、Windowsのパスを表現する場合にも便利である。 local path = [=[C:\Windows\Fonts]=] ]==]<WtXmlEndTag />

2.2.5. Perl
-----------

Perlでは、ヒアドキュメントを実現する方法が何通りか存在する。ヒアドキュメントを引用符で囲むと、一重引用符ならヒアドキュメント内の変数展開が行われないようになり、二重引用符では変数が展開される。またバッククオートならヒアドキュメントの中身がシェルで実行されるというように、普通の文字列を囲む引用符と同様にヒアドキュメントの機能が変化する。 ヒアドキュメントを終わらせる識別子は、行頭に書かなければその役割を果たさない。

なお、ヒアドキュメントは開始の識別子を書いた直後ではなく、その次の行頭から始まるので、ヒアドキュメントを含む命令の続きは識別子の後ろへ書くこととなる。

ダブルクオートで識別子を囲むと、以下のようになる。

my $sender = "Buffy the Vampire Slayer"; my $recipient = "Spike";

print <<"END";

Dear $recipient,

I wish you to leave Sunnydale and never return.

Not Quite Love, $sender

END<WtXmlEndTag />

出力は以下のようになる。

Dear Spike,

I wish you to leave Sunnydale and never return.

Not Quite Love, Buffy the Vampire Slayer

上の例をシングルクォートにすると、

print <<'END'; Dear $recipient,

I wish you to leave Sunnydale and never return.

Not Quite Love, $sender END<WtXmlEndTag />

出力は以下のようになる。

Dear $recipient,

I wish you to leave Sunnydale and never return.

Not Quite Love, $sender

バッククオートで囲んだ例を以下に示す。

my $shell_script_stdout = <<`END`; echo foo echo bar END

1行で複数のヒアドキュメントを始めることもできる。

say; This is the beginning: BEGIN And now it is over! END

以下と同じ動作となるsay;<WtXmlEndTag />

タグ自体に空白文字を含めれば、ヒアドキュメントの終了タグをインデントすることもできる。

say <<' END'; Hello World END

2.2.6. PHP
----------

PHPにもヒアドキュメントが存在するが、開始の不等号は3つとなっている。

<?php

$name = "Joe Smith"; $occupation = "Programmer"; echo <<<EOF

This is a heredoc section. For more information talk to $name, your local $occupation.

Thanks!

EOF;

$toprint = <<<WtXmlEndTag />

出力は以下のようになる。

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!

ヒアドキュメントの後に続くプログラムは、ヒアドキュメントの終端IDの次の行から書くことになるが、文がヒアドキュメントの直後で終わる場合には、終端IDの直後にセミコロンを書くことができる。この場合のセミコロンを除いて、終端IDと同じ行に余計な文字を書いてしまうと、それは終端として処理されなくなってしまう。正しい終端が見つからないと、PHPはスクリプトの最後でパースエラーとなる。

PHP 5.3以降では、ヒアドキュメントの識別子を一重引用符で囲むことで、内部での変数展開が行われない、Nowdocという機能も登場している。

$x = <<<'END' Dear $recipient,

I wish you to leave Sunnydale and never return.

Not Quite Love, $sender END;<WtXmlEndTag /> さらに、同じくPHP 5.3以降では、ヒアドキュメントを開始する識別子を二重引用符で囲むこともできるようになっている。ただし、ヒアドキュメントの動作としては何も囲まない場合と同じである。

2.2.7. Python
-------------

Pythonでは、区切り文字として引用符を3つ続ける形での文字列リテラルをサポートしている。これらの文字列リテラルは途中に改行を入れることができるなど、ヒアドキュメントとしての特徴を持っている。

Perlの例と同様の動作をするものをPython 3で書くと、以下のようになる。

message="""Dear {recipient},

I wish you to leave Sunnydale and never return.

Not Quite Love, {sender} """ print)<WtXmlEndTag />

Python 3.0未満では、printは関数ではなくキーワードとして存在している。また、Templateクラスは変数置換と同様の機能を果たし、引用符3つの文字列と組み合わせて使うこともできる。

2.2.8. R
--------

R言語では通常の文字列リテラルに改行を入れることができるが、変数置換には対応していない。文字列をtextConnection関数に与えることでファイル識別子として扱えるようになり、下の例のようにソースコードに埋め込んだデータを表の形で処理することができる。

str <- "State Population Income Illiteracy Life.Exp Murder HS.Grad Frost Alabama 3615 3624 2.1 69.05 15.1 41.3 20 Alaska 365 6315 1.5 69.31 11.3 66.7 152 Arizona 2212 4530 1.8 70.55 7.8 58.1 15 Arkansas 2110 3378 1.9 70.66 10.1 39.9 65" x <- read.table, header=TRUE, row.names=1)

2.2.9. Racket
-------------

Racketでは、「ヒア文字列」として、#<<の後に終端の識別子を続ける形の書式が存在する。文字列の中身には、#<<直後の行から終端の識別子だけの行の手前まで、全てのものが含まれる。

lang racket(displayln

<<HERESTRINGRacketによるヒア文字列のサンプル。

その1
その2
その3HERESTRING )<WtXmlEndTag />

出力は以下のようになる。

Racketによるヒア文字列のサンプル。

その1
その2
その3ヒア文字列の中でエスケープシーケンスは処理されず、何を書いても文字通りに認識される。

lang racket(displayln

<<Racketのヒア文字列 ☺この文字列は複数行からなっており、 またUnicodeにあるどんな文字でも使うことができる。 €や☠、♡に①でも一向にかまわない。

この次の行が終端だが、終端の区切り文字にもUnicode文字ならなんでも使える。 Racketのヒア文字列 ☺ )<WtXmlEndTag />

上の例を実行すると、以下のように出力される。

この文字列は複数行からなっており、 またUnicodeにあるどんな文字でも使うことができる。 €や☠、♡に①でも一向にかまわない。

この次の行が終端だが、終端の区切り文字にもUnicode文字ならなんでも使える。

通常の文字列が使えるところなら、たいていヒア文字列を使うことができる。

lang racket(printf #<<END Dear ~a,

Thanks for the insightful conversation ~a.

~a

END "Isaac" "yesterday" "Carl")<WtXmlEndTag />

出力は以下のようになる。

Dear Isaac,

Thanks for the insightful conversation yesterday.

Carl

at-exp拡張を使うことで、@記法を使えるようになる。 具体的には、以下のように書ける。

lang at-exp racket))<WtXmlEndTag />

出力は以下のようになる。

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.2.10. Ruby
------------

Rubyにもヒアドキュメントが存在し、それは <<識別子 を含む行の次の行から識別子だけの行の直前までを文字列とする行指向のリテラルである。例えば、

print <<EOS # 識別子 EOS までがリテラルになる the string next line EOS

のように書くと、以下と同じように出力される。 print " the string

next line"<WtXmlEndTag />

ヒアドキュメントでは、開始ラベル<<識別子が文法要素としての式に当たる。つまり、開始ラベルを使ってヒアドキュメント全体を引数に渡したりレシーバにしたりすることができる。

式の中に開始ラベルを書く
method の第二引数には " ヒアドキュメント" が渡される method ヒアドキュメント LABEL

ヒアドキュメントをレシーバにメソッドを呼ぶp < "THE LOWER CASE STRING "<WtXmlEndTag />

開始ラベルの次の行は常にヒアドキュメントとなる。 printf # <- この行はヒアドキュメントに含まれてしまう This line is a here document. EOS

開始ラベルを<<-識別子のように - を付けて書くことで終端行をインデントすることができるが、これ以外では、終端行に、余分な空白やコメントさえも書くことはできない。 if need_define_foo eval <<-EOS # '<<-' を使うと…… def foo print "foo " end EOS

↑終端行をインデントできます。end<WtXmlEndTag />

Ruby 2.3以降では、開始ラベルを <<~識別子 のように ~ を付けて書くことで、以下のようなヒアドキュメントを書くことができる。 expected_result = <<~SQUIGGLY_HEREDOC This would contain specially formatted text.

That might span many lines SQUIGGLY_HEREDOC<WtXmlEndTag /> 最もインデントが少ない行を基準にして、全ての行の先頭から空白が取り除かれる。インデントの深さを決定するために主にタブやスペースで構成された行は無視される。ただし、エスケープされたタブやスペースは通常の文字と同じように扱われる。

また、一行に複数のヒアドキュメントを書くことも可能である。 print <<FIRST, <<SECOND これは一つめのヒアドキュメントです。 まだ一つめです。 FIRST この行からは二つめのヒアドキュメントです。 この行で終わります。 SECOND

開始ラベル <<識別子 の 識別子 を引用符で囲むことで、ヒアドキュメントとなる文字列リテラルの性質は対応する文字列リテラルと同じ扱いとなる。つまり、一重引用符で囲めばヒアドキュメントは変数展開やエスケープシーケンスの処理も行われない。また、二重引用符であれば通常のヒアドキュメントと同様、変数展開やエスケープシーケンスが処理される。バッククォートで囲めば、ヒアドキュメントの内容がシェルで実行される。

バックスラッシュ記法、式展開が有効print <<"EOS" The price is #{$price}. EOS

上のものと同じ結果print <<EOS The price is #{$price}. EOS

式展開はできないprint <<'EOS' The price is #{$price}. EOS

コマンドを実行print <<`EOC` date diff test.c.org test.c EOC<WtXmlEndTag />

2.2.11. Tcl
-----------

Tclには、通常の文字列中に改行を入れることができるため、「ヒアドキュメント」としての特別な文法構造は存在しない。中括弧で区切った場合、変数置換は行われない。

puts { Grocery list

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

1. Salad mix. 2. Strawberries.* 3. Cereal. 4. Milk.*

Organic}<WtXmlEndTag />

二重引用符で区切った場合、変数置換が実行時に行われる。

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 "<WtXmlEndTag />

中括弧区切りの場合、エスケープしない中括弧については左右のバランスがとれている必要がある。二重引用符区切りの場合は中括弧のバランスは無関係となるが、バックスラッシュ・ドル記号・角カッコにはそれぞれに特別な機能が存在する。また、エスケープされていない二重引用符が現れたところで文字列が終了する。

なお、上の2つの例では、いずれも文字列の最初と最後の文字が改行文字となる。もしもこれが邪魔なら、string trimで取り除くことができる。

puts [string trim " Dear $recipient,

I wish you to leave Sunnydale and never return.

Not Quite Love, $sender " ]<WtXmlEndTag />

同様に、string mapをインデントの解除や中括弧のエスケープなど、書き方を工夫する上で使うことができる。

2.3. プログラミング言語以外
----------------

2.3.1. Microsoft NMAKE
----------------------

[1]では、ヒアドキュメントと同様な概念として「インラインファイル」が存在する。インラインファイルは<<あるいは<<ファイル名という書式で始まる。 そして、<<だけの行で終わるが、生成したファイルを保存するかのオプションを指定する場合は<<KEEPあるいは<<NOKEEPのようになる。

target0: dependent0 command0 << 一時インラインファイル ... <<

target1: dependent1 command1 << 一時ファイルだけど処理後にも残るインラインファイル ... <<KEEP

target2: dependent2 command2 <<filename2 名前付きだけど処理後に削除されるインラインファイル ... <<NOKEEP

target3: dependent3 command3 <<filename3 名前付きインラインファイル ... <<KEEP<WtXmlEndTag />

2.3.2. Data URI Scheme
----------------------

たいていのブラウザで、「data:」で始まるURIとして別なドキュメントを埋め込むというData URI schemeが実装されている。

2.3.3. Job Control Language
---------------------------

Job Control Language  では、使用するデータを指定するDD文において「DD *」を指定すると、以降は80バイトのカードイメージのデータと解釈される。このデータは、1カラム目から2カラム目に「/*」と記述するか、次のJCLステートメントの記述が始まることで終了する。

3. 脚注
-----

4. 関連項目
-------

tr 
パイプ 
文字列