ヒアドキュメント
ヒアドキュメント(別の呼び方としてヒア文字列、heredocなど)は、文字列リテラルを、シェルスクリプトやプログラミング言語のソースコード中に埋め込むための1つの方法である。
ヒアドキュメントが構文に存在する言語として、sh、csh、ksh、Bash、zshなどUnixシェルのシェルスクリプトやPerl、PHP、Python、Rubyといったスクリプト言語があるが、これらに限られるものではない。改行やホワイトスペースなどが、書いたとおりに適用される。言語によっては、ヒアドキュメント内で変数展開やコマンド呼び出しを行える場合もある。
ヒアドキュメントのいちばん一般的な記法としては、<<の後に区切り文字となる識別子を続け、その次の行から対象となる文字列を書き、そして最初に指定した識別子だけの行で終わりとなる。Unixシェルでは、コマンドの入力を与えるのに使われることが多い。
以下の節では、言語・環境ごとに実装の詳細を記述する。記法としては、Unixシェルと似たものが多いが、環境によっては別な構文で同等の機能を果たすことができたり、同様の構文を別な名前で呼んでいたりする例がある。また、一部の例ではシンタックスハイライトが正しく効いていないことがあるので、注意が必要である。
ヒアドキュメントは標準入力として扱われるため、標準入力を扱えるコマンドと組み合わせて使用する必要がある。文字列リテラルやファイルしか受け取らないようなコマンドで使用する場合は、変数を介して渡したり、bash固有の機能のProcess Substitutionを利用したりする必要がある。
以下の例では、ヒアドキュメントのテキストがtrコマンドへの入力となっている。
ここではEND_TEXTを区切り文字としてあり、ヒアドキュメントの始めと終わりの位置を規定している。下2行のONE TWO THREEとUNO DOS TRESは、trコマンドを実行して出力された結果である。
ヒアドキュメントを開始するところで<<-とすると、行頭のタブが無視されるようになり、シェルスクリプトのインデントを崩さずにヒアドキュメントを書けるようになる(なお、コマンドラインにタブ文字を入力するには特殊な操作が必要となる。また、以下の例はタブではなくスペースで書いてあるので、そのままコピペしても動作しない)。
デフォルトで、変数展開やバッククオートによるコマンド展開も行われる。
これらは、区切り文字を引用符で囲むことで無効にできる。
また、bash、ksh、zshではヒアストリングという機能も存在する。
Windows PowerShellでは、同様な機能がヒア文字列と呼ばれている。ヒア文字列は開始識別子の@"あるいは@'で始まり、対応する"@または'@だけの行で終わりとなり、間に入ったものすべてが文字列に含まれる。@"~"@のヒア文字列では変数展開が有効となるが、@'~'@では変数展開はなされない。ヒア文字列内の変数展開は、$xのような単純な変数、あるいは$(式)という形の式が対象となる。
以下のPowerShellコードでは、ヒア文字列が関数への引数となっている。
出力は以下のとおりである。
ONE TWO THREE
EINS ZWEI DREI
次の例では、二重引用符で囲まれたヒア文字列内の変数展開やコマンドの実行が行われている。
出力は以下のとおりである。
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 $($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#では逐語的文字列リテラル (verbatim string literal) と呼ばれる。アットマーク@をプレフィックスとする。正規表現のパターン文字列の記述などにも便利である。
C++11では、Raw文字列リテラルという機能が導入されている。Raw文字列リテラルは、Rをプレフィックスとし、続く"識別子(と)識別子"の間に書く。識別子としては、0文字から16文字の範囲内で、
空白文字、括弧、バックスラッシュ以外の文字ならソースコードに書ける任意の文字を使って付けることができる。
D言語では、バージョン2.0以降で、qをプレフィックスとした文字列がサポートされている。これらの文字列では、括弧 (()、<>、{}、[]のいずれか)あるいは改行の直後に識別子を置いて終端を示す。
括弧で囲まれるものは、このようになる。
識別子によるものは、このようになる。
Luaでは、文字列をとで挟んで文字列リテラルとすることができ、この間では改行もそのまま適用されるほか、エスケープ文字も認識されない。この文字列に他の目的で]を含ませられないという問題があったので、
Lua 5.1では、開始地点となる2つの[の間に等号をいくつか入れて、ちょうど同じ数だけ等号を挟んだ]まで終わらせない、ということができるようになった。
Perlでは、ヒアドキュメントを実現する方法が何通りか存在する。ヒアドキュメントを引用符で囲むと、一重引用符ならヒアドキュメント内の変数展開が行われないようになり、二重引用符では変数が展開される。またバッククオートならヒアドキュメントの中身がシェルで実行されるというように、普通の文字列を囲む引用符と同様にヒアドキュメントの機能が変化する(何も囲まなければ変数展開が行われる)。
ヒアドキュメントを終わらせる識別子は、行頭に書かなければその役割を果たさない。
なお、ヒアドキュメントは開始の識別子を書いた直後ではなく、その次の行頭から始まるので、ヒアドキュメントを含む命令の続きは識別子の後ろへ書くこととなる。
ダブルクオートで識別子を囲むと、以下のようになる。
出力は以下のようになる。
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
バッククオートで囲んだ例を以下に示す(環境依存)。
1行で複数のヒアドキュメントを始めることもできる。
タグ自体に空白文字を含めれば、ヒアドキュメントの終了タグをインデントすることもできる。
PHPにもヒアドキュメントが存在するが、開始の不等号は3つとなっている。
出力は以下のようになる。
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という機能も登場している。
さらに、同じくPHP 5.3以降では、ヒアドキュメントを開始する識別子を二重引用符で囲むこともできるようになっている。ただし、ヒアドキュメントの動作としては何も囲まない場合と同じである。
Pythonでは、区切り文字として引用符を3つ続ける( あるいは """)形での文字列リテラルをサポートしている。これらの文字列リテラルは途中に改行を入れることができるなど、ヒアドキュメントとしての特徴を持っている。
Perlの例と同様の動作をするものをPython 3で書くと、以下のようになる。
Python 3.0未満では、printは関数ではなくキーワードとして存在している。また、Templateクラスは変数置換と同様の機能を果たし、引用符3つの文字列と組み合わせて使うこともできる。
R言語では通常の文字列リテラルに改行を入れることができるが、変数置換には対応していない。文字列をtextConnection()関数に与えることでファイル識別子として扱えるようになり、下の例のようにソースコードに埋め込んだデータを表の形で処理することができる。
Racketでは、「ヒア文字列」として、#<<の後に終端の識別子を続ける形の書式が存在する。文字列の中身には、#<<直後の行から終端の識別子だけの行の手前まで、全てのものが含まれる(終端の識別子の直前にある改行は文字列の一部とはならない)。
出力は以下のようになる。
Racketによるヒア文字列のサンプル。
* その1
* その2
* その3
ヒア文字列の中でエスケープシーケンスは処理されず、何を書いても文字通りに認識される。
上の例を実行すると、以下のように出力される。
この文字列は複数行からなっており、
またUnicodeにあるどんな文字でも使うことができる。
€や☠、♡に①でも一向にかまわない。
この次の行が終端だが、終端の区切り文字にもUnicode文字ならなんでも使える。
通常の文字列が使えるところなら、たいていヒア文字列を使うことができる。
出力は以下のようになる。
Dear Isaac,
Thanks for the insightful conversation yesterday.
at-exp拡張を使うことで、@記法を使えるようになる。
具体的には、以下のように書ける。
出力は以下のようになる。
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 2.3以降では、開始ラベルを <<~識別子 のように ~ を付けて書くことで、以下のようなヒアドキュメントを書くことができる。
最もインデントが少ない行を基準にして、全ての行の先頭から空白が取り除かれる。インデントの深さを決定するために主にタブやスペースで構成された行は無視される。ただし、エスケープされたタブやスペースは通常の文字と同じように扱われる。
また、一行に複数のヒアドキュメントを書くことも可能である。
開始ラベル <<識別子 の 識別子 を引用符で囲むことで、ヒアドキュメントとなる文字列リテラルの性質は対応する文字列リテラルと同じ扱いとなる。つまり、一重引用符で囲めばヒアドキュメントは変数展開やエスケープシーケンスの処理も行われない。また、二重引用符であれば通常のヒアドキュメントと同様、変数展開やエスケープシーケンスが処理される。バッククォートで囲めば、ヒアドキュメントの内容がシェルで実行される。
Tclには、通常の文字列中に改行を入れることができるため、「ヒアドキュメント」としての特別な文法構造は存在しない。中括弧で区切った場合、変数置換は行われない。
二重引用符で区切った場合、変数置換が実行時に行われる。
中括弧区切りの場合、エスケープしない中括弧については左右のバランスがとれている必要がある。二重引用符区切りの場合は中括弧のバランスは無関係となるが、バックスラッシュ・ドル記号・角カッコにはそれぞれに特別な機能が存在する。また、エスケープされていない二重引用符が現れたところで文字列が終了する。
なお、上の2つの例では、いずれも(書いてあるとおりに)文字列の最初と最後の文字が改行文字となる。もしもこれが邪魔なら、string trimで取り除くことができる。
同様に、string mapをインデントの解除や中括弧のエスケープなど、書き方を工夫する上で使うことができる。
では、ヒアドキュメントと同様な概念として「インラインファイル」が存在する。インラインファイルは<<(一時ファイル)あるいは<<ファイル名(指定のファイルを作成/上書き)という書式で始まる。
そして、<<だけの行で終わるが、生成したファイルを保存するかのオプションを指定する場合は<<KEEPあるいは<<NOKEEP(大文字小文字は問わない)のようになる。
たいていのブラウザで、「data:」で始まるURIとして別なドキュメントを埋め込むというData URI schemeが実装されている。
Job Control Language (JCL) では、使用するデータを指定するDD文において「DD *」を指定すると、以降は80バイトのカードイメージのデータと解釈される。このデータは、1カラム目から2カラム目に「/*」と記述するか、次のJCLステートメント(1カラム目から2カラム目が「//」で始まる行)の記述が始まることで終了する。