シェルスクリプトで処理を行う際、複数行のテキストをファイルに出力し、それを読み込ませたいという場面が良くあります。そんな時、一行ずつechoを実行するよりもヒアドキュメントでの記述にすればすっきりと書けてコードの可読性を上げる事が出来ます。
Shellスクリプト基礎知識(全11記事+1)
├─シェルスクリプトの基本事項!
├─変数と特殊変数について!
├─演算子「算術演算子」「比較演算子」について!
├─条件分岐「if」「case」について!
├─ループ処理「for」「while」について!
├─文字列置換「bash」「sed」について!
├─複数行のテキスト出力!ヒアドキュメントについて!
├─書式?戻り値?シェルスクリプト内の関数について!
├─シェルの組み込みコマンドについて!
├─クォートとは?コマンド置換とは?実現方法と内容の違いについて!
└─リダイレクトとは?標準入力・出力、標準エラー出力等について!
(補足)シェルスクリプトの設計書とは?必要な項目や書き方等を解説!
ヒアドキュメントとは?
ヒアドキュメントは、コマンドの標準入力に一定の文書を入力する書式コマンドです。
一定の内容の文書を即席で作成して、これをコマンドの標準入力にリダイレクトしたいことが時々あります。そんな時、このヒアドキュメントは非常に便利です。
コマンドの書式
「コマンド」 <<[-] 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 18 19 | [root@CentOS7 ~]# str="あいうえお" [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)は、必ず行頭に記述する必要があります。終了文字列の前にタブや半角スペースを含めると、シェルはそれを終了文字列として認識しません。
一方で、<<- を使用すると、ヒアドキュメント本体の各行の行頭にあるタブ文字を無視することができます。ただし、この機能は終了文字列には適用されません。
スペース:⬜️
$ str="文字列"
$ cat <<- EOF
⬜️${str}
⬜️hoge
⬜️fuga
EOF 👈 終了文字列は常に行頭に書く
終了文字列について
- 終了文字列の位置:
ヒアドキュメントの終了文字列(例: EOF)は、行頭に記述する必要があります。 - インデントの影響:
終了文字列の前にタブやスペースなどのインデントがあると、シェルはそれを終了文字列として認識しません。 - <<- の効果:
<<- を使用すると、ヒアドキュメント本体の各行の行頭にあるタブ文字を無視できますが、終了文字列自体には影響を与えません。
正しい例1(ヒアドキュメント本体の行頭をタブでインデント)
タブ:🟪
cat <<- EOF
🟪ここはヒアドキュメント本文です。
EOF
出力結果
1 2 3 4 | [root@CentOS7 -]# cat <<- EOF > ここはヒアドキュメント本文です。 > EOF ここはヒアドキュメント本文です。 👈 タブは無視されて行頭から文字列が反映されている |
上記の例では、ヒアドキュメント本体の各行はタブでインデントされていますが、<<- によりそのタブは無視されます。実行結果にタブが無視されて行頭から文字列が反映されます。
正しい例2(hereドキュメント本体の行頭をスペースでインデント)
スペース:⬜️
cat <<- EOF
⬜️ここはヒアドキュメント本文です。
EOF
出力結果
1 2 3 4 | [root@CentOS7 -]# cat <<- EOF > ここはヒアドキュメント本体です。 > EOF ここはヒアドキュメント本体です。👈 スペースは無視されない |
上記の例では、ヒアドキュメント本体の各行はスペースでインデントされていますが、<<- はスペースを無視しません。実行結果にスペース分の空白が反映されます。
間違った例1 (終了文字列の前にタブ)
ヒアドキュメントの誤った使い方例(終了文字列の前にタブ)
スペース:⬜️
タブ:🟪
cat <<- EOF
⬜️ここはヒアドキュメント本文です。
🟪 👈 EOFの行は認識されない
出力結果
1 2 3 4 | [root@CentOS7 -]# cat <<- EOF > ここはヒアドキュメント本文です。 > EOF > |
終了文字列(例: EOF)の前にタブが含まれている場合、シェルはそれを終了文字列として認識しません。その結果、ヒアドキュメントの終了を認識できず、スクリプトは待受状態のままとなります。
間違った例2(終了文字列の前にスペース)
ヒアドキュメントの誤った使い方例(終了文字列の前にスペース)
タブ:🟪
cat <<- EOF
⬜️ここはヒアドキュメント本文です。
⬜️ 👈 EOFこの行は認識されない
出力結果
1 2 3 4 | [root@CentOS7 -]# cat <<- EOF > ここはヒアドキュメント本文です。 > EOF > |
終了文字列(例: EOF)の前にスペースが含まれている場合、シェルはそれを終了文字列として認識しません。その結果、ヒアドキュメントの終了を認識できず、スクリプトは待受状態のままとなります。
終了文字列(例: EOF)は必ず行頭に記述してください。スペースやタブが含まれていると、終了文字列として認識されません。
ヒアドキュメントを使ったサンプルスクリプト
ヒアドキュメントを使うことで、シェルスクリプトで複数行の文字列を簡単に扱えるようになります。
簡単な文字列出力例
以下はヒアドキュメントを使って複数行の文字列を表示するシンプルな例です。
cat <
これはヒアドキュメントの基本例です。
複数行の文字列を簡単に扱うことができます。
EOF
出力結果
1 2 3 4 5 6 7 | [root@CentOS7 -]# cat <<EOF これはヒアドキュメントの基本例です。 複数行の文字列を簡単に扱うことができます。 EOF これはヒアドキュメントの基本例です。 複数行の文字列を簡単に扱うことができます。 |
設定ファイルの生成
ヒアドキュメントを使えば、スクリプト内で設定ファイルを簡単に生成することができます。
cat < config.txt
[Settings]
theme=dark
timeout=30
EOF
生成されるファイル内容
1 2 3 4 5 6 7 8 9 10 | [root@CentOS7 -]# cat <<EOF > config.txt > [Settings] > theme=dark > timeout=30 > EOF [root@CentOS7 -]# cat config.txt [Settings] theme=dark timeout=30 |
SQLクエリの実行
以下は、データベース操作を自動化する際の例です。このスクリプトは、ユーザー情報を取得し、特定の条件に基づいて更新します。
mysql -u username -p database <<EOF
SELECT * FROM users WHERE age > 30;
UPDATE users SET active = 1 WHERE last_login > '2023-01-01';
EOF
クエリの実行結果
1 2 3 4 5 6 7 8 9 | +----+--------+-----+-------------+ | ID | Name | Age | Active | +----+--------+-----+-------------+ | 1 | Alice | 35 | 0 | | 2 | Bob | 40 | 1 | +----+--------+-----+-------------+ mysql> UPDATE users SET active = 1 WHERE last_login > '2023-01-01'; Query OK, 1 row affected |
FTPスクリプトの自動化
ヒアドキュメントを活用することで、FTPクライアントを自動化し、ファイルのアップロードやダウンロードを効率化できます。
ftp -n <<EOF
open ftp.example.com
user username password
put localfile.txt
bye
EOF
説明
ftp -n: 自動ログインを無効化(スクリプト内でログイン情報を提供)。
open ftp.example.com: FTPサーバーに接続。
user username password: ユーザー名とパスワードで認証。
put localfile.txt: ローカルファイル localfile.txt をサーバーにアップロード。
bye: 接続を終了。
実行結果
FTPサーバーにファイルが正しくアップロードされます。
自動化により、手動で行う手間が省けます。
サーバー設定の簡略化
ヒアドキュメントは、サーバー構成ファイルや設定ファイルの生成にも役立ちます。複雑な設定内容を簡単にスクリプト化できます。
cat <<EOF > /etc/httpd/conf.d/example.conf
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/html
ErrorLog /var/log/httpd/example.com-error.log
CustomLog /var/log/httpd/example.com-access.log combined
</VirtualHost>
EOF
説明
cat <<EOF > /etc/httpd/conf.d/example.conf: 設定内容を指定したファイルに書き込み。
<VirtualHost> ブロック内に、サーバー名やログの設定を記述。
実行結果
/etc/httpd/conf.d/example.conf に仮想ホスト設定ファイルが生成されます。
サーバー再起動後、example.com が適切に動作します。
ヒアドキュメントを補完する便利なコマンド
ヒアドキュメントは複数行の文字列を簡単に扱える便利な機能ですが、その効果をさらに高めるには、他のシェルコマンドと組み合わせることが重要です。
ここでは、ヒアドキュメントを補完し、シェルスクリプトでの作業を効率化するための便利なコマンドを紹介します。これらのコマンドを活用すれば、スクリプトの作成やデータの操作がさらに柔軟で簡単になります
echoコマンドとの違い
「ヒアドキュメント」以外にも、「echo」コマンドを使って標準出力を行うことが可能です。echo コマンドは、引数として指定された文字列を出力するシンプルなコマンドで、複数行の出力を行いたい場合、少し工夫が必要です。
最も単純な方法は、echo コマンドを複数回実行することです。例えば、次のように記述すれば、各コマンドごとに改行されます。
$ echo hoge
$ echo fuga
$ echo doga
しかし、複数行を出力する際に、1行ずつ echo コマンドを実行するのは冗長に感じられる場合があります。このような場合、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 が含まれており、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 ~]# |
ちなみに「echo $TEST」をダブルクォーテーションでくくると改行されて表示されます。
まとめ
ヒアドキュメントの使い方について、説明してきました。ヒアドキュメントは、使い方によっては非常にパワフルで書式なコマンドとなります。
サーバーOSの設定ファイルから、スクリプトの外部ファイルの操作まで、ヒアドキュメントの存在はかなり重宝します。ヒアドキュメントを覚えておいて、損はありません! 積極的に覚えていきましょう!