Bashシェルスクリプトのヒアドキュメントとは一体何か?

Bash

はじめに

シェルスクリプトを書いていてよく利用する記法として「ヒアドキュメント」があります。

Wikipediaには「文字列リテラルをソースコード中に埋め込む方法のひとつ」という記載がありますが、
「文字列リテラル」という認識でいると echo しようとして何も表示されないというミスをすることもあります。

検証環境

$ bash -version | head -n 1
GNU bash, バージョン 5.0.7(1)-release (x86_64-apple-darwin18.5.0)

ヒアドキュメントを使って文字列を出力する

文字列を出力したい場合、ヒアドキュメントを使って出力させる方法があります。
複数行にまたがるような長い文字列を出力したい場合に利用されることが多いです。

以下は cat コマンドを利用してヒアドキュメントを出力させる例です。

$ cat <<EOF
Hello World!

My name is ${USER}.
EOF

実行結果は以下のようになります。

Hello World!

My name is genzouw.

ヒアドキュメントに埋め込まれた「変数」は展開されます。

echo では出力できない

文字列を出力するというイメージからついつい echo を使ってしまうことがありますが、これはNGです。

$ echo <<EOF
Hello World!

My name is ${USER}.
EOF

上記のコマンドを実行しても何も出力されません。

ヒアドキュメントとは一体何か?

本題にはいります。 cat で表示できたことから「ファイル」または「標準入力」のいずれかの扱いになるかと思いますが、以下のようなPHPスクリプトを作成してヒアドキュメントを食わせてみます。

test.php

<?php

# test.php というファイル名で作成
echo '== ARGS', PHP_EOL;
var_dump($argv);

echo '== STDIN', PHP_EOL;
while (($line = fgets(STDIN)) !== false) {
    echo $line;
}

大きく以下の2つの情報を出力しています。

  • コマンドライン引数 (PHPでは $argv で取得できる。添字 0 はスクリプト名が格納される。)
  • 標準入力 (PHPでは STDIN 定数を使って標準入力を読み込むことができる。)

実行結果は以下のようになります。

$ php test.php <<EOF
Hello World!

My name is ${USER}.
EOF

== ARGS
array(1) {
  [0]=>
  string(8) "test.php"
}
== STDIN
Hello World!

My name is genzouw.

ヒアドキュメントは「標準入力」ですね。

パイプで渡しても同じ?

パイプで渡しても同じ結果を得られます。

先程の例は以下のコマンドと同様です。

# 改行を除外するために「バックスラッシュ」を2箇所埋め込んでいます
$ echo """\
Hello World!

My name is ${USER}.\
""" | php test.php

先程の echo を使ってしまったケースは以下のような処理を実行したのと同じということになります。

# 改行を除外するために「バックスラッシュ」を2箇所埋め込んでいます
$ echo """\
Hello World!

My name is ${USER}.\
""" | echo

何も出力されないわけです。

ひとこと

Bashシェルスクリプトにおける ヒアドキュメント は「文字列リテラル」ではなく「標準入力」である!

Bash