シェルスクリプトで処理を行う際、複数行のテキストをファイルに出力し、それを読み込ませたいという場面が良くあります。そんな時、一行ずつechoを実行するよりもヒアドキュメントでの記述にすればすっきりと書けてコードの可読性を上げる事が出来ます。
ヒアドキュメントとは?
ヒアドキュメントは、コマンドの標準入力に一定の文書を入力する書式コマンドです。
一定の内容の文書を即席で作成して、これをコマンドの標準入力にリダイレクトしたいことが時々あります。そんな時、このヒアドキュメントは非常に便利です。
コマンドの書式
「コマンド」 <<[-] EOF・・・終了文字
<ヒアドキュメント本文>
EOF・・・終了文字
ヒアドキュメントでは、次の行の行頭から終了文字列(EOF)が現れる直前の行までをヒアドキュメント本文とし、その内容がコマンドの標準入力にリダイレクトされます。
「<<」の右側の終了文字列(EOF)がクォート「'」されている場合は、ヒアドキュメント本文の展開は行われず、変数を処理できません。
始まりの文字はEOF固定と言うわけではなく好きな文字を設定する事が可能ですが、「EOF(エンドオブファイル)」が一般的です。
標準入力へリダイレクト
下記の書式は、「'」で囲んだ場合のヒアドキュメントと「’」で囲まない場合のヒアドキュメントの2つのパターン記載しています。
str="あいうえお"
# クォーテーションで囲んだヒアドキュメント
$ cat << 'EOF'
${str} 👈 変数
hoge
fuga
EOF
# クォーテーションで囲まないヒアドキュメント(変数展開)
$ cat << EOF
${str} 👈 変数
hoge
fuga
EOF
2つのパターンのヒアドキュメントを実行してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[root@CentOS7 ~]# cat << 'EOF' > ${str} > hoge > fuga > EOF ${str} 👈 変数が展開されていない hoge fuga [root@CentOS7 ~]# cat << EOF > ${str} > hoge > fuga > EOF あいうえお 👈 変数が展開されている hoge fuga [root@CentOS7 ~]# |
クォーテーションで囲んだヒアドキュメントは変数展開を行いません。逆にクォーテーションで囲まないヒアドキュメントは変数展開を行っていることが分かります。
メモ
設定を行う対象が、1台2台なら設定ファイルを直接手書きで編集するのも良いのですが、実際のプロジェクトでは300台のサーバーを設定するなど普通にあります。ヒアドキュメントを習得することで、省力化を測ることができます!
ヒアドキュメントのインデント
ヒアドキュメントを扱う際に、必ず聞かれる質問第一位が「インデントが設定できない!」と言う問題です。タブを使ってインデントする場合、コマンドが正しく実行できません。
「<<」の直後に「-」を付けた場合、ヒアドキュメント本体の行頭のタブが無視されるようになります。重要なことは「終了文字列(EOF)」がタブでインデントされていることです。半角スペースでは無視されません。
# タブ:■
# 半角スペース:□
$ cat <<- EOF
□□${str}
□□hoge
□□fuga
■EOF 👈 「□□(半角スペース)」でインデントすると、エラーとなります。
1 2 3 4 5 6 7 8 |
[root@CentOS7 bin]# cat <<- EOF > ${str} > hoge > fuga EOF 👈 インデントされていないように見えますが、実際には「■(タブ)」があります。 あいうえお hoge fuga |
ヒアドキュメントは非常に有用なコマンド書式と言う事が分かると思います。
echoコマンドとの違い
「ヒアドキュメント」以外でも、「echo」コマンドを使って標準出力することが可能です。
echoコマンド自体は引数として指定した文字列リテラル(=文字列としてみなされる定数)を「1行ずつ」出力するコマンドなので、改行したいときには少し工夫が要ります。
もっとも単純に考えれば、echoコマンドを順番に複数回実行すれば、1回のecho実行ごとに改行がされるため、目的は達成できますがやや「そうじゃない!」感が漂います。
$ echo hoge
$ echo fuga
$ echo doga
他の方法、例えばbashの場合echoコマンドでは"-eオプションを使用"し、"改行コードを引数に含めて、連続して各行の内容を入力する"ことで、改行をしながら入力する事が出来ます。
$ echo -e "hoge\nfuga\ndoga"
「echo」コマンドの実行結果
1 2 3 4 5 |
[root@CentOS7 ~]# echo -e "hoge\nfuga\ndoga" hoge fuga doga [root@CentOS7 ~]# |
実行結果の出力が「hoge」「fuga」「doga」と3行に分かれて出力されています。
これは""(ダブルクォーテーション)の中に「\n」が含まれており、「\n」がbashでエスケープ文字として解釈されずに改行コードとして出力された事によります。
ちなみに今度は「""(ダブルクォーテーション)」が無い状態と「-e」オプションが無い状態で実行してみます。
# 「""(ダブルクォーテーション)」が無い状態
$ echo -e hoge\nfuga\ndoga
# 「-e」オプションが無い状態
$ echo "hoge\nfuga\ndoga"
1 2 3 4 5 |
[root@CentOS7 ~]# echo -e hoge\nfuga\ndoga hogenfugandoga [root@CentOS7 ~]# echo "hoge\nfuga\ndoga" hoge\nfuga\ndoga [root@CentOS7 ~]# |
このようにechoコマンドだけでも複数行のテキスト出力は出来ますが、多くの行を入力するときには不便かつ可読性も落ちます。
ココに注意
シェルの種類によっては、-eオプションは使用できないものもあるので、スクリプトを書くときの環境、シェルには注意してください。
ファイルへ出力する方法
ヒアドキュメントを使って、ファイルへ複数行のテキスト出力を行いたい場合はどうすればよいでしょうか?
これまでの例(上記)では、catは 入力元を明示しない場合は、標準入力から入力を受け付けるため、ヒアドキュメントは標準入力として扱われている事が分かります。
入力があれば出力先を指定するのがcatコマンドの文法のため、以下のようにファイルへリダイレクトする、という処理を記述すればファイルへ出力することが可能です。
下記にファイル出力する場合の記述をしてみます。
# ヒアドキュメントを使ってファイルへ出力する
$ cat << EOF > ./aaa.txt
hoge
fuga
EOF
「echo」コマンドの引数としてヒアドキュメントの書式を用いても同様の結果が出るように思われますが、echoコマンドは標準入力から読み込みを行わなず、引数の文字列リテラルのみを出力するため、今回のケースでは何も出力されません。
1 2 3 4 5 6 7 8 9 |
[root@CentOS7 ~]# cat << EOF > ./aaa.txt > hoge > fuga > EOF [root@CentOS7 ~]# ls -l -rw-r--r--. 1 root root 10 4月 16 10:42 aaa.txt 👈 ファイルが作成されている [root@CentOS7 ~]# cat aaa.txt 👈 ファイルの中身を確認 hoge fuga |
この特性をうまく使いこなせば、OSの設定ファイル等の面倒な設定が一発で、差し替え可能となります。
catコマンドを利用したヒアドキュメントの記述
catコマンドは標準入力を受け付けるため、ヒアドキュメントと組み合わせて使う事が多いですが、他にも様々な使い道があります。
例えば、ヒアドキュメントは標準入力として扱われる事からそのままでは変数として保存できません。
そんな時は、宣言した変数内へコマンド置換でcatコマンドを変数に設定し、その入力としてヒアドキュメントを記載するようにすれば目的を達成できます。
# 宣言した変数内へコマンド置換でcatコマンドを変数に設定
$ TEST=$(cat << EOF
hoge
fuga
EOF
)
1 2 3 4 5 6 7 8 |
[root@CentOS7 ~]# TEST=$(cat << EOF > hoge > fuga > EOF > ) [root@CentOS7 ~]# echo $TEST hoge fuga [root@CentOS7 ~]# |
まとめ
ヒアドキュメントの使い方について、説明してきました。ヒアドキュメントは、使い方によっては非常にパワフルで書式なコマンドとなります。
サーバーOSの設定ファイルから、スクリプトの外部ファイルの操作まで、ヒアドキュメントの存在はかなり重宝します。ヒアドキュメントを覚えておいて、損はありません! 積極的に覚えていきましょう!
Shellスクリプト基礎知識(全11記事+1)
├─シェルスクリプトの基本事項!
├─変数と特殊変数について!
├─演算子「算術演算子」「比較演算子」について!
├─条件分岐「if」「case」について!
├─ループ処理「for」「while」について!
├─文字列置換「bash」「sed」について!
├─複数行のテキスト出力!ヒアドキュメントについて!
├─書式?戻り値?シェルスクリプト内の関数について!
├─シェルの組み込みコマンドについて!
├─クォートとは?コマンド置換とは?実現方法と内容の違いについて!
└─リダイレクトとは?標準入力・出力、標準エラー出力等について!
(補足)シェルスクリプトの設計書とは?必要な項目や書き方等を解説!