トラック
/
Bash
Bash
/
シラバス
/
ヒアドキュメント
ヒア

ヒアドキュメント の Bash

{other: "%{count}個の演習"}

ヒアドキュメントについて

Bashスクリプトでは、「ヒアドキュメント」(「heredoc」とも呼びます)を使うと、複数行の入力を、まるでターミナルに直接入力しているかのように、コマンドやプログラムに渡すことができます。 外部ファイルや複雑な文字列操作を必要とせず、スクリプトの中に複数行のテキストを埋め込める強力な仕組みです。

主な特徴と構文

  1. 区切り文字:ヒアドキュメントは、<<演算子に続けて区切りとなる単語(「マーカー」や「ターミネーター」とも呼ばれます)を書くところから始まります。 この区切り文字は好きな単語でかまいませんが、わかりやすさのためにEOFやEND、TEXTなどを使うのが一般的です。 コードをより読みやすくしたいなら、END_INSTALLATION_INSTRUCTIONSのように、内容を表す説明的な単語を区切り文字にするとよいでしょう。

  2. 内容:最初の<< DELIMITERのあとに、リダイレクトしたい内容を書きます。 テキストでもコードでも、何行になってもかまいません。

  3. 終了:区切り文字が、前後に空白のない単独の行として再び現れると、ヒアドキュメントは終わります。

基本的な構文

command << DELIMITER
  Content line 1
  Content line 2
  ...
  Content line N
DELIMITER

仕組み

  • Bashは、開始の<< DELIMITERと終了のDELIMITERのあいだにあるすべての行を読み取ります。
  • Bashは、この内容をコマンドの標準入力に接続します。
  • コマンドは、まるでキーボードから入力されたかのように、この入力を処理します。

例1:シンプルなテキストの出力

cat << EOF
This is the first line.
This is the second line.
This is the third line.
EOF

出力:

This is the first line.
This is the second line.
This is the third line.

この例では:

  • catがコマンドです。
  • << EOFで、EOFを区切り文字としてヒアドキュメントを開始します。
  • 3行のテキストが内容です。
  • 単独の行にあるEOFがヒアドキュメントを終了します。
  • その後、catが受け取った内容を出力します。

例2:wc(単語数カウント)と組み合わせる

wc -l << END
Line 1
Line 2
Line 3
END

出力:

3

ここでは、wc -lが行数を数えています。 ヒアドキュメントが、その3行を入力として渡します。

例3:スクリプトにデータを渡す

スクリプト:

#!/usr/bin/env bash

# Script to process input
while IFS= read -r line; do
  echo "Processing: $line"
done

対話的なbashプロンプトから、ヒアドキュメントを使ってスクリプトを呼び出します:

./your_script << MY_DATA
Item 1
Item 2
Item 3
MY_DATA

出力:

Processing: Item 1
Processing: Item 2
Processing: Item 3

バリエーションと高度な機能

内容をそのまま扱う

Bashは、ヒアドキュメントの中でも変数展開、コマンド置換、算術展開を行います。 この意味で、ヒアドキュメントは二重引用符で囲んだ文字列のように振る舞います。

cat << EOF
The value of HOME is $HOME
The current date is $(date)
Two plus two is $((2 + 2))
EOF

出力:

The value of HOME is /home/glennj
The current date is Thu Apr 24 13:47:32 EDT 2025
Two plus two is 4

区切り文字を引用符で囲むと(シングルクォートでもダブルクォートでも)、これらの展開は行われません。 内容はそのままの文字として扱われます。 これはシングルクォートで囲んだ文字列のようなものです。

cat << 'EOF'
The value of $HOME is not expanded here.
The result of $(date) is not executed.
Two plus two is calculated by $((2 + 2))
EOF

出力:

The value of $HOME is not expanded here.
The result of $(date) is not executed.
Two plus two is calculated by $((2 + 2))

先頭のタブを取り除く

<<の代わりに<<-(末尾にハイフンが付きます)を使うと、Bashはヒアドキュメントの各行の先頭にある_タブ文字_を取り除きます。 これは、出力に影響を与えずに、スクリプトの中でヒアドキュメントの内容をインデントしたいときに便利です。

# Note, the leading whitespace is tab characters only, not spaces!
# The ending delimiter can have leading tabs as well.
cat <<- END
	This line has 1 leading tab.
	  This line has a leading tab and some spaces.
		This line 2 leading tabs.
	END

出力は、先頭のタブをすべて取り除いた状態で表示されます:

This line has 1 leading tab.
    This line has a leading tab and some spaces.
This line 2 leading tabs.
Caution

著者はこの使い方をあまりおすすめしていません。 スクリプトが読みやすくなることはありますが、

  1. タブ文字を誤ってスペースに置き換えてしまいがちです(エディターが自動でやってしまうこともあります)。また、
  2. スペースとタブの違いに気づくのが難しいです。

ヒアドキュメントを使う場面

  • 複数行の入力:コマンドに複数行のテキストを渡したいとき。
  • 設定ファイル:小さな設定の断片をスクリプトの中に埋め込みたいとき。
  • コードの生成:スクリプトの中でその場でコードを作り出したいとき。
  • 対話の自動化:対話的なプログラムに対して、ユーザーの入力を模擬したいとき。
  • 外部ファイルの回避:一時ファイルを作りたくないとき。

よくある使い方としては、ヘルプのテキストを表示することがあります:

#!/usr/bin/env bash

usage() {
    cat << END_USAGE
Refresh database tables.

usage: ${0##*/} [-h|--help] [-A|--no-archive]

where: --no-archive flag will _skip_ the archive jobs
END_USAGE
}

# ... parsing command line options here ...

if [[ $flag_help == "true" ]]; then
  usage
  exit 0
fi

考えられるデメリット

  • 埋め込むドキュメントが大きくなると、コードが読みにくくなります。 スクリプトといっしょに、ドキュメントを別のファイルとして配布したほうがよいこともあります。
  • ヒアドキュメントは、コードの流れを途切れさせることがあります。 プログラムにテキストを渡したい場所が、深く入れ子になったコードの中だったりするかもしれません。 そのとき、ヒアドキュメントのインデントが、周りのコードと比べてちぐはぐに見えることがあります。

ヒアストリング

ヒアドキュメントと同じように、_ヒアストリング_もコマンドに入力を渡します。 ただし、ヒアドキュメントがテキストのブロックとして渡されるのに対し、ヒアストリングは1つの文字列として渡されます。 ヒアストリングでは<<< "text"という構文を使います。

tr 'a-z' 'A-Z' <<< "upper case this string"

出力:

UPPER CASE THIS STRING

ヒアドキュメントと違って、終了の区切り文字は必要ありません。

なぜヒアストリングを使うのか?

ヒアストリングの代わりにパイプを使うこともできます:

echo "upper case this string" | tr 'a-z' 'A-Z'

では、なぜヒアストリングを使うのでしょうか?

長い時間のかかる計算の出力として文字列を得て、その結果を2つの別々のコマンドに渡したい場合を考えてみましょう。 パイプを使うと、その計算を2回実行しなければなりません:

some_long_running_calculation | first_command
some_long_running_calculation | second_command

より効率的な方法は、その計算の出力を(コマンド置換を使って)変数に保存し、ヒアストリングを使って後続の2つのコマンドに入力を渡すことです:

result=$( some_long_running_calculation )
first_command <<< "$result"
second_command <<< "$result"

この例を実際の場面に当てはめたものが、次のものです:

  • ページネーションされたREST APIの問い合わせに対するJSONレスポンスを取得し、
  • そのJSONデータをjqプログラムに渡して結果を解析させ、ファイルに出力し、さらに
  • 同じJSONデータを別のjqプログラムに渡して、次の問い合わせのURLを決めます。
# initialize the output CSV file
echo "ID,VALUE" > data.csv

url='https//example.com/api/query?page=1'

while true; do
  json=$( curl "$url" )

  # convert the results part of the response into CSV
  jq -r '.results[] | [.id, .value] | @csv' <<< "$json"

  # get the URL for the next page
  url=$( jq -r '.next_url // ""' <<< "$json" )
  if [[ "$url" == "" ]]; then
    break
  fi
done >> data.csv

出力リダイレクトの位置に注目してください。 whileループからの出力はすべて、ファイルdata.csvに追記されます。

リダイレクトとしてのヒアドキュメントとヒアストリング

これらはリダイレクトの一種にすぎないので、ほかのリダイレクト操作と組み合わせることができます:

cat <<< END_OF_TEXT > output.txt
This is my important text.
END_OF_TEXT

awk '...' <<< "$my_var" >> result.csv

まとめ

ヒアドキュメント(「heredoc」)は、Bashスクリプトで複数行の入力を扱うための、柔軟で便利な方法です。 テキストやデータをスクリプトの中に直接埋め込む手間を減らし、スクリプトをより自己完結的で読みやすいものにしてくれます。

ヒアストリングはヒアドキュメントに似ていますが、よりシンプルで動的な構文を提供します。

GitHubで編集 リンクは新しいウィンドウまたはタブで開きます