ヒアドキュメント
ヒアドキュメント(別の呼び方としてヒア文字列、heredocなど)は、文字列リテラルを、シェルスクリプトやプログラミング言語のソースコード中に埋め込むための1つの方法である。 概要ヒアドキュメントが構文に存在する言語として、sh、csh、ksh、Bash、zshなどUnixシェルのシェルスクリプトやPerl、PHP、Python、Rubyといったスクリプト言語があるが、これらに限られるものではない。改行やホワイトスペースなどが、書いたとおりに適用される。言語によっては、ヒアドキュメント内で変数展開やコマンド呼び出しを行える場合もある。 ヒアドキュメントのいちばん一般的な記法としては、 言語ごとの実装以下の節では、言語・環境ごとに実装の詳細を記述する。記法としては、Unixシェルと似たものが多いが、環境によっては別な構文で同等の機能を果たすことができたり、同様の構文を別な名前で呼んでいたりする例がある。また、一部の例ではシンタックスハイライトが正しく効いていないことがあるので、注意が必要である。 コマンドラインシェルUNIXシェルヒアドキュメントは標準入力として扱われるため、標準入力を扱えるコマンドと組み合わせて使用する必要がある。文字列リテラルやファイルしか受け取らないようなコマンドで使用する場合は、変数を介して渡したり、bash固有の機能のProcess Substitutionを利用したりする必要がある。 以下の例では、ヒアドキュメントのテキストが $ tr a-z A-Z <<END_TEXT
> one two three
> uno dos tres
> END_TEXT
ONE TWO THREE
UNO DOS TRES
ここでは ヒアドキュメントを開始するところで<<-とすると、行頭のタブが無視されるようになり、シェルスクリプトのインデントを崩さずにヒアドキュメントを書けるようになる(なお、コマンドラインにタブ文字を入力するには特殊な操作が必要となる。また、以下の例はタブではなくスペースで書いてあるので、そのままコピペしても動作しない)。 $ 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
Windows PowerShellWindows PowerShellでは、同様な機能がヒア文字列と呼ばれている。ヒア文字列は開始識別子の 以下のPowerShellコードでは、ヒア文字列が関数への引数となっている。 function ConvertTo-UpperCase($string) { $string.ToUpper() }
ConvertTo-UpperCase @'
one two three
eins zwei drei
'@
出力は以下のとおりである。 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 $($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! 一重引用符でヒア文字列を書いた場合、出力は以下のようになる。 $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#では逐語的文字列リテラル (verbatim string literal) と呼ばれる。アットマーク using System.Text.RegularExpressions;
...
string s = @"1. Item One
2. Item Two
3. Item Three";
System.Console.WriteLine(s);
string path1 = "C:\\temp\\test.txt";
string path2 = @"C:\temp\test.txt";
System.Console.WriteLine(path1);
System.Console.WriteLine(path2);
System.Console.WriteLine(path1 == path2);
System.Console.WriteLine(@"<button id=""btn1""/>"); // 逐語的文字列内にダブルクォーテーションを含める場合。
System.Console.WriteLine(Regex.Match(path1, "^.+\\.(.+)$").Success);
System.Console.WriteLine(Regex.Match(path2, @"^.+\.(.+)$").Success);
C++C++11では、Raw文字列リテラルという機能が導入されている。Raw文字列リテラルは、 // Raw文字列内のエスケープシーケンスは展開されない。
char const *a = R"('\n'というエスケープシーケンスは改行文字を表す。)";
// 識別子を使えば、Raw文字列の中に同様な表現を入れることができる。
wchar_t const *b = LR"...(Raw文字列はR"(...)"のように書く)...";
char16_t const *b = uR"xyz(
"\u20AC"のような、Unicode文字を表すエスケープシーケンスも
Raw文字列中では展開されないので、ソースコードのエンコードとして適切なものを選んで、
"€"のように直接書く以外に、Raw文字列中に入れる方法はない。
)xyz";
DD言語では、バージョン2.0以降で、 括弧で囲まれるものは、このようになる。 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 );
}
LuaLuaでは、文字列を local ls = [[
最初の改行は文字列の一部とはならない。
つまり、この文字列は2行である。]]
-- 等号を使った記法(ネスト可能)
local lls = [==[
この記法は、Windowsのパスを表現する場合にも便利である。
local path = [=[C:\Windows\Fonts]=]
]==]
PerlPerlでは、ヒアドキュメントを実現する方法が何通りか存在する[1]。ヒアドキュメントを引用符で囲むと、一重引用符ならヒアドキュメント内の変数展開が行われないようになり、二重引用符では変数が展開される。またバッククオートならヒアドキュメントの中身がシェルで実行されるというように、普通の文字列を囲む引用符と同様にヒアドキュメントの機能が変化する(何も囲まなければ変数展開が行われる)。 ヒアドキュメントを終わらせる識別子は、行頭に書かなければその役割を果たさない。 なお、ヒアドキュメントは開始の識別子を書いた直後ではなく、その次の行頭から始まるので、ヒアドキュメントを含む命令の続きは識別子の後ろへ書くこととなる。 ダブルクオートで識別子を囲むと、以下のようになる。 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
出力は以下のようになる。 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
出力は以下のようになる。 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(<<BEGIN . "this is the middle\n" . <<END);
This is the beginning:
BEGIN
And now it is over!
END
# 以下と同じ動作となる
say("This is the beginning:\nthis is the middle\nAnd now it is over!\n");
タグ自体に空白文字を含めれば、ヒアドキュメントの終了タグをインデントすることもできる。 say <<' END';
Hello World
END
PHPPHPにもヒアドキュメントが存在するが、開始の不等号は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 = <<<EOF
Hey $name! You can actually assign the heredoc section to a variable!
EOF;
echo $toprint;
?>
出力は以下のようになる。 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はスクリプトの最後でパースエラーとなる[2]。 PHP 5.3以降では、ヒアドキュメントの識別子を一重引用符で囲むことで、内部での変数展開が行われない、Nowdocという機能も登場している[2]。 $x = <<<'END'
Dear $recipient,
I wish you to leave Sunnydale and never return.
Not Quite Love,
$sender
END;
さらに、同じくPHP 5.3以降では、ヒアドキュメントを開始する識別子を二重引用符で囲むこともできるようになっている。ただし、ヒアドキュメントの動作としては何も囲まない場合と同じである。 PythonPythonでは、区切り文字として引用符を3つ続ける( Perlの例と同様の動作をするものをPython 3で書くと、以下のようになる。 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'))
Python 3.0未満では、 RR言語では通常の文字列リテラルに改行を入れることができるが、変数置換には対応していない。文字列を 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(textConnection(str), header=TRUE, row.names=1)
RacketRacketでは、「ヒア文字列」として、 #lang racket
(displayln
#<<HERESTRING
Racketによるヒア文字列のサンプル。
* その1
* その2
* その3
HERESTRING
)
出力は以下のようになる。 Racketによるヒア文字列のサンプル。 * その1 * その2 * その3 ヒア文字列の中でエスケープシーケンスは処理されず、何を書いても文字通りに認識される。 #lang racket
(displayln
#<<Racketのヒア文字列 ☺
この文字列は複数行からなっており、
またUnicodeにあるどんな文字でも使うことができる。
€や☠、♡に①でも一向にかまわない。
この次の行が終端だが、終端の区切り文字にもUnicode文字ならなんでも使える。
Racketのヒア文字列 ☺
)
上の例を実行すると、以下のように出力される。 この文字列は複数行からなっており、 またUnicodeにあるどんな文字でも使うことができる。 €や☠、♡に①でも一向にかまわない。 この次の行が終端だが、終端の区切り文字にもUnicode文字ならなんでも使える。 通常の文字列が使えるところなら、たいていヒア文字列を使うことができる。 #lang racket
(printf #<<END
Dear ~a,
Thanks for the insightful conversation ~a.
~a
END
"Isaac"
"yesterday"
"Carl")
出力は以下のようになる。 Dear Isaac, Thanks for the insightful conversation yesterday. Carl
#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 @記法は文字列限定の機能ではなく、他の場面でも使うことができる。 RubyRubyにもヒアドキュメントが存在し、それは print <<EOS # 識別子 EOS までがリテラルになる
the string
next line
EOS
のように書くと、以下と同じように出力される。 print " the string\n next line\n"
ヒアドキュメントでは、開始ラベル # 式の中に開始ラベルを書く
# method の第二引数には " ヒアドキュメント\n" が渡される
method(arg1, <<LABEL, arg2)
ヒアドキュメント
LABEL
# ヒアドキュメントをレシーバにメソッドを呼ぶ
p <<LABEL.upcase
the lower case string
LABEL
# => "THE LOWER CASE STRING\n"
開始ラベルの次の行は常にヒアドキュメントとなる。 printf('%s%d',
<<EOS,
3055 * 2 / 5) # <- この行はヒアドキュメントに含まれてしまう
This line is a here document.
EOS
開始ラベルを if need_define_foo
eval <<-EOS # '<<-' を使うと……
def foo
print "foo\n"
end
EOS
#↑終端行をインデントできます。
end
Ruby 2.3以降では、開始ラベルを expected_result = <<~SQUIGGLY_HEREDOC
This would contain specially formatted text.
That might span many lines
SQUIGGLY_HEREDOC
最もインデントが少ない行を基準にして、全ての行の先頭から空白が取り除かれる。インデントの深さを決定するために主にタブやスペースで構成された行は無視される。ただし、エスケープされたタブやスペースは通常の文字と同じように扱われる。 また、一行に複数のヒアドキュメントを書くことも可能である。 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
TclTclには、通常の文字列中に改行を入れることができるため、「ヒアドキュメント」としての特別な文法構造は存在しない。中括弧で区切った場合、変数置換は行われない。 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
"
中括弧区切りの場合、エスケープしない中括弧については左右のバランスがとれている必要がある。二重引用符区切りの場合は中括弧のバランスは無関係となるが、バックスラッシュ・ドル記号・角カッコにはそれぞれに特別な機能が存在する。また、エスケープされていない二重引用符が現れたところで文字列が終了する。 なお、上の2つの例では、いずれも(書いてあるとおりに)文字列の最初と最後の文字が改行文字となる。もしもこれが邪魔なら、 puts [string trim "
Dear $recipient,
I wish you to leave Sunnydale and never return.
Not Quite Love,
$sender
" \n]
同様に、 プログラミング言語以外Microsoft NMAKEMicrosoft NMAKEでは、ヒアドキュメントと同様な概念として「インラインファイル」が存在する[7]。インラインファイルは target0: dependent0
command0 <<
一時インラインファイル
...
<<
target1: dependent1
command1 <<
一時ファイルだけど処理後にも残るインラインファイル
...
<<KEEP
target2: dependent2
command2 <<filename2
名前付きだけど処理後に削除されるインラインファイル
...
<<NOKEEP
target3: dependent3
command3 <<filename3
名前付きインラインファイル
...
<<KEEP
Data URI Scheme→詳細は「Data URI scheme」を参照
たいていのブラウザで、「data:」で始まるURIとして別なドキュメントを埋め込むというData URI schemeが実装されている。 Job Control LanguageJob Control Language (JCL) では、使用するデータを指定するDD文において「 脚注
関連項目 |