zshでシェルスクリプト：複数行コメントアウトしたいんじゃ！

# お忙しい方のための一例
以下で一応それっぽいことできます
```shell
#!/bin/zsh

echo "1行だけコメントアウトしたいときは#を使う！"
# echo "ここはコメント!"

echo "複数行コメントアウトしたいときは : << 　の後、'任意の文字列'で囲む！" 

: << 'COMMENT'
echo "ここはコメント！"
echo "ここはコメント！"
echo "ここはコメント!"
COMMENT

echo "ここは表示されるよ！" 

```

でも行頭に全部`#`付けるのがベターっぽい。

# 新しいプログラミング言語学ぶときって
新しいプログラミング言語勉強するときに1番最初に何を調べますか？
チュートリアルに従うと大体"Hello,world!"の出力からはじまるのでprint文的なものを最初に目にすると思うのですが、私はそれと同時にどうしても知りたくなるものがあります。
それは、
**コメントアウトの仕方**です。

最近シェルスクリプトを書く機会が身近になりました。
シェルはPCがMacでデフォルトがzshだからという理由だけでzshを使っています。
「シェルスクリプト？.shってやつ？なんか最初にコメントみたいなの書いてるやつよね」
くらいの認識だったがためにちまちま勉強しつつ書いているのですが1行コメントアウトの仕方はすぐにわかった一方で、複数行コメントアウトの仕方が全然ズバッと出てこない！
というわけで、いろいろ調べていろいろ実験したので記事にしてみようと思います。

以下ダラダラ書くので、よろしければお付き合いください。

# 軽い気持ちでググってみた
コメントアウトの仕方ぐらいググれば出てくるやろ〜と思っていたので
`zsh 複数行　コメントアウト`でググってみました。
結果、出てくるものbashの記事ばっかり！
まあでも...大体同じなんじゃないん？と思ったので[ググって1個目に出てきたbashのコメントアウトのやり方](https://qiita.com/kaw/items/ffdaf5d23627c0bd7e87)をzshで試してみました。

```shell:test_1_zsh.sh
#!/bin/zsh

echo "ここは表示されるよ！" 

<< COMMENT
echo "ここはコメント！"
echo "ここはコメント！"
echo "ここはコメント!"
COMMENT

echo "ここは表示されるよ！" 

```

以下結果
```
$ zsh test_1_zsh.sh
ここは表示されるよ！
echo "ここはコメント！"
echo "ここはコメント！"
echo "ここはコメント!"
ここは表示されるよ！
```

**なんか、めっちゃ表示されたやん...**

# マニュアルを読んでみる
全然コメントアウトできんやんけ！！ってことで、こういうときは公式を見ます。
`man zsh`をターミナルで実行するといろいろ情報出してくれるのですね。
zshのホームページ([https://www.zsh.org/](https://www.zsh.org/))からたどっていって
ドキュメンテーション([https://zsh.sourceforge.io/Doc/](https://zsh.sourceforge.io/Doc/))を参照します。

読み進めると[6.7 Comments](https://zsh.sourceforge.io/Doc/Release/Shell-Grammar.html#Comments)にコメントについての記載があります。
短いので以下に全文引用
>In non-interactive shells, or in interactive shells with the INTERACTIVE_COMMENTS option set, a word beginning with the third character of the histchars parameter (‘#’ by default) causes that word and all the following characters up to a newline to be ignored.

**複数行のコメントアウトについて一切触れられていない、だと、、**

# じゃあbashはどうなん？
じゃあbashはどう書かれているんだろう？と気になって調べてみました。
`man bash`を実行するとマニュアル表示されて、その中にコメントについての記載がありました。
>COMMENTS
       In  a  non-interactive  shell,  or an interactive shell in which the
       interactive_comments option to the shopt  builtin  is  enabled  (see
       SHELL  BUILTIN  COMMANDS below), a word beginning with # causes that
       word and all remaining characters on that line to  be  ignored.   An
       interactive  shell  without  the interactive_comments option enabled
       does not allow comments.  The interactive_comments option is  on  by
       default in interactive shells.

**い、一緒のこと書いてあるーー！！！**

# シェルスクリプトには複数行コメントアウトというものが存在していない
ドキュメンテーションに何も書いていない時点でお察しなのですが、
調べていく中で、まず大前提としてシェルスクリプトには
複数行コメントアウトというものが存在していないということがわかりました。

じゃあbashの複数行コメントアウトの例として出てきていた
`<<`からはじまるやつはなんなんだという話になってくるのですが
**ヒアドキュメント**というものを使って便宜的に複数行コメントアウトを実現しているものだということです。

## `<<` ヒアドキュメント(Here Documents)
bashは`man bash`の中に、zshはドキュメンテーションの[7 Redirection](https://zsh.sourceforge.io/Doc/Release/Redirection.html#Redirection)に記載があります。
活用方法は以下の記事に詳しいです。大変参考になります。
[【参考】bashのヒアドキュメントを活用する](https://qiita.com/take4s5i/items/e207cee4fb04385a9952)

`<<`ではじめ、それに続くワード(任意)で囲まれた範囲の行に記載されているものがヒアドキュメントと呼ばれるとのことで、以下のようなイメージです。
```shell
<< WORD
ここの行から
ヒアドキュメント
WORD
# 上のWORDの行まで。WORD自身は特に読み込まれたりすることはない。
```

## コメントアウトとして使う：bashでの挙動
さてこれがコメントアウトとして働いてくれるのかということですが、
以下のコードを実行してみましょう。
なんか記事が全体的に長くなってきた気がするのでコードは折り畳んじゃいます。

<details><summary>bashのサンプルコード</summary>

```shell:test_2_bash.sh
#!/bin/bash

echo "ヒアドキュメントのテスト"

num=1

echo "--------------"

echo "なにもなし"

<< here_zsh
これは文字列だよ
$num
ans_1=$((2 * 2))
echo "$ans"
here_zsh

echo "ans_1の値:$ans_1"

echo "--------------"

echo "クオーテーションあり"

<< 'here_zsh'
これは文字列だよ
$num
ans_2=$((2 * 2))
here_zsh

echo "ans_2の値:$ans_2"

echo "--------------"

echo "バックスラッシュをつける"
<< \here_zsh
これは文字列だよ 
$num 
ans_3=$((2 * 2)) 
here_zsh

echo "ans_3の値:$ans_3"

```

</details>


以下実行結果
```
$ bash test_2_bash.sh
ヒアドキュメントのテスト
--------------
なにもなし
ans_1の値:
--------------
クオーテーションあり
ans_2の値:
--------------
バックスラッシュをつける
ans_3の値:
```

囲んで書いた部分は特に出力されないようです。

## コメントアウトとして使う：zshでの挙動
ではzshではどうでしょうか？
シバン以外はbashと全く同じものですが一応下にコードをば。

<details><summary>zshのサンプルコード</summary>

```shell:test_2_zsh.sh
#!/bin/zsh

echo "ヒアドキュメントのテスト"

num=1

echo "--------------"

echo "なにもなし"

<< here_zsh
これは文字列だよ
$num
ans_1=$((2 * 2))
echo "$ans"
here_zsh

echo "ans_1の値:$ans_1"

echo "--------------"

echo "クオーテーションあり"

<< 'here_zsh'
これは文字列だよ
$num
ans_2=$((2 * 2))
here_zsh

echo "ans_2の値:$ans_2"

echo "--------------"

echo "バックスラッシュをつける"
<< \here_zsh
これは文字列だよ 
$num 
ans_3=$((2 * 2)) 
here_zsh

echo "ans_3の値:$ans_3"


```
</details>

以下実行結果

```
$ zsh test_2_zsh.sh
ヒアドキュメントのテスト
--------------
なにもなし
これは文字列だよ
1
ans_1=4
echo ""
ans_1の値:
--------------
クオーテーションあり
これは文字列だよ
$num
ans_2=$((2 * 2))
ans_2の値:
--------------
バックスラッシュをつける
これは文字列だよ 
$num 
ans_3=$((2 * 2)) 
ans_3の値:
```

ヒアドキュメントも出力されました。
さらにドキュメントに
>If any character of word is quoted with single or double quotes or a ‘\’, no interpretation is placed upon the characters of the document.Otherwise, parameter and command substitution occurs,

と記載されている通り
`here_zsh`をクオーテーションで囲むなどしなかった場合には
計算が行われるなどしています。

計算されるだけで代入はされない、その後の処理に影響していない、
という点を見ればコメントアウトできているとも言えるのかもしれないですが、
出力されてしまうのでどうもこりゃ上手くいってない感があります。

# zshで複数行コメントアウト
前置きが大変長くなってしまったのですが、bashでは`<<`から始まるもので複数行コメントアウトっぽいことができると確認できました。
一方で中身が出力されてしまうzshはどうすれば良いのでしょうか？

いろいろ調べていく中で出会ったstack overflowの回答にコレ！というものがありました。
[【stack overflow】Block Comments in a Shell Script](https://stackoverflow.com/questions/947897/block-comments-in-a-shell-script)

## `: [ arg ... ]` で何もしない
zshでもbashでも同じのようなのですが、行頭にコロンを置くとそれに続く記述の処理は何もせず終了コードを返すという処理になるそうです。
zshのドキュメントでは[17 Shell Builtin Commands](https://zsh.sourceforge.io/Doc/Release/Shell-Builtin-Commands.html#Shell-Builtin-Commands)に記載があります。
以下コロンに関する記述を全文引用
>: [ arg ... ]
This command does nothing, although normal argument expansions is performed which may have effects on shell parameters. A zero exit status is returned.

何もしないけどコマンドの展開はしてしまうので`: << COMMENT`ではなく`: << 'COMMENT'`もしくは`: << \COMMENT`で始めるのがベターという議論。これはzshだけでなくbashも同じなので、bashにおいてコメントアウトできてるっぽく見える`<< COMMENT`で始まるものも出力されていないだけで内部的にはコマンドの展開がされているらしい。。。おそろしや

というわけで一応のソリューションが冒頭のコメントアウト方法だということで落ち着きました。
```shell
echo "複数行コメントアウトしたいときは : << 　の後、'任意の文字列'で囲む！" 

: << 'COMMENT'
echo "ここはコメント！"
echo "ここはコメント！"
echo "ここはコメント!"
COMMENT

echo "ここは表示されるよ！" 
```

### すっごい参考になったので追記
`:`の扱いについて本当にとても参考になる記事に、この記事を推敲している段階でぶち当たったのでリンクを貼ります。
[何もしない組み込みコマンド ":" （コロン）の使い道](https://qiita.com/xtetsuji/items/381dc17241bda548045d#%E3%83%92%E3%82%A2%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%81%A8%E7%B5%84%E3%81%BF%E5%90%88%E3%82%8F%E3%81%9B%E3%81%9F%E6%93%AC%E4%BC%BC%E7%9A%84%E3%81%AA%E8%A4%87%E6%95%B0%E8%A1%8C%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88%E3%81%A8%E3%81%97%E3%81%A6)

特に「何か書かなければならないところに仮置きする」というのはめっちゃなるほどとなりました。
これまで当たり障りない処理とかで誤魔化してたので、、、精進せねばですね。

## stack overflowでされているその他のコメントアウト法議論
[前出のstack overflow](https://stackoverflow.com/questions/947897/block-comments-in-a-shell-script)では上記のものの他にもいくつか複数行コメントアウト法が議論されていましたので、いかにいくつか挙げてみます。

### `:`のより簡素な使い方
`:`以下をシングルクオーテーションで囲む方法
コメントアウトを解除するときなんか消しそびれそうで怖いなと思いました。

```shell
#!/bin/zsh

: '
echo "このやり方でも"
echo "コメントアウトみたいにできる"
'
```

### if文を使ういくつかの方法
条件が偽であればなんでもいいので、if文使って書くのは複数派閥がある様子。
```shell
#!/bin/zsh

# 1と0はイコールじゃないもんね
if [ 1 -eq 0 ]; then
    echo "このやり方でも"
    echo "コメントアウトみたいにできる"
fi

# もはや条件何も書かない
if [ ]; then
    echo "このやり方でも"
    echo "コメントアウトみたいにできる"
fi

# これが一番みてわかりやすい！と言ってる人も
if false; then
    echo "このやり方でも"
    echo "コメントアウトみたいにできる"
fi

```

### エイリアスに書いちゃう
これまでに挙げた方法をエイリアスに書いちゃう派の人もいるようです。

```shell
#!/bin/zsh

# コロン
alias BEGINCOMMENT_COLON=": << 'COMMENT'"
alias ENDCOMMENT_COLON="COMMENT"

BEGINCOMMENT_COLON
    echo "このやり方でも"
    echo "コメントアウトみたいにできる"
ENDCOMMENT_COLON

# if文
alias BEGINCOMMENT_IF="if [ ]; then"
alias ENDCOMMENT_IF="fi"

BEGINCOMMENT_IF
    echo "このやり方でも"
    echo "コメントアウトみたいにできる"
ENDCOMMENT_IF

```


### ブロック選択モードにして行頭全部に`#`つけりゃええやん

以下stack overflowより引用

>In all honesty, why so much overengineering...
I consider it really a bad practice to write active code for generating passive code.
My solution: most editors have block select mode. Just use it to add # to all lines you want to comment out. What's the big deal...
Notepad example:
To create: Alt - mousedrag down, press #.
To delete: Alt-mousedrag down, shift-right arrow, delete.

ごもっともでございます。

VSCodeならalt+shift押しつつ範囲選択ですね。

# まとめ

+ シェルスクリプトには複数行コメントアウトは正式には存在していない
+ zshにおいてはコロンとヒアドキュメントを使ってそれっぽいことすることが可能
+ bashにおいてはヒアドキュメントを使うだけでそれっぽいことすることが可能
+ ヒアドキュメントの開始行はコメントアウトしたい内容がコマンド展開されないように`'COMMTNT'`とするか`\COMMENT`としておくのがベター
+ 他にもいくつか複数行コメントアウトっぽくする方法あるのでお好みで
+ でもちゃんとしたコメントアウトじゃないからコメントアウトしたい全部の行頭に`#`付けるのが一番よい

思いのほか長くなってしまった。
お付き合いいただきありがとうございました！