シバン shebang がないシェルスクリプトはエラーになることがある。どのシェルで実行されるかわからないからちゃんと書いとけ。

## はじめに

シバンがないシェルスクリプトは `sh` で動作するとか、シバンがない方が可搬性が高いという神話がありますが、これは間違いです。シバンを書いていない場合、想定外のシェルでシェルスクリプトが実行されることがあり、場合によってはエラーが発生します。したがってシバンは書いた方がより多くの環境で動きます。

シバンは POSIX で標準化されていませんが、これは単にシバンを書いたときの動作が当時は環境によって異なっていため移植性があると認めることができず標準化できなかったに過ぎません。シバンを書くと環境によって異なる動きになるため POSIX 的には移植性がないと宣言せざるを得ませんが、現実的にはシバンを書いた方がより多くの環境で動きます。この記事ではその理由を説明します。

この記事は「[シバン (shebang) を書いた方が可搬性が高いシェルスクリプトになる](https://qiita.com/ko1nksm/items/c244752c7f3d0df5e723)」のまとめです。ぶっちゃけタイトルが気に入らなくて内容も詳細すぎたので。（というか読者ターゲットが違う。）

## シバンってなに？

この記事を読もうとする人がシバンを知らないとは思えませんが、スクリプト言語（シェルスクリプトに限らない）でファイルの一行目に書く `#!` で始まる行のことです。スクリプトに実行権限をつけてスクリプト名で実行すると、シバンで指定したコマンド（インタプリタ）上でスクリプトが実行されます。

## シバンがないシェルスクリプトはどのシェルで動くか？

シバンがないシェルスクリプトを実行したときに使われるシェルは **`sh` ではなくシェル依存**です。つまり現在使用しているシェルによってスクリプトを実行するシェルが異なります。

### 検証方法

シバンなしの以下のスクリプトをいろんなシェル上で実行して、**シバンのないシェルスクリプトが `sh` で実行されるとは限らない**ことをその出力から判断しています。基本的に macOS 上で検証していますが Ubuntu 上でも同じ挙動をしているようです。

```sh:script
ps
```

### 検証結果

| POSIX シェル        | 実行シェル（実行コマンド）     | 特殊条件下での実行シェル        |
| ------------------- | ------------------------------ | ------------------------------- |
| dash 0.5.11.4       | `dash`                         |                                 |
| bash 5.1.4          | `bash`                         |                                 |
| ksh 2020            | `ksh`                          |                                 |
| mksh R59            | `/bin/sh ./script`             |                                 |
| zsh 5.8             | `sh ./script`                  |                                 |
| yash 2.51           | `sh - ./script` (補足 A)       |                                 |
| **非 POSIX シェル** | **実行シェル（実行コマンド）** | **特殊条件下での実行シェル**    |
| tcsh 6.21.00        | `/bin/sh ./script`             | ⚠ `/bin/tcsh ./script` (補足 B) |
| fish 3.2.2          | ⚠ エラー（補足 E）             | `/bin/sh ./script` (補足 C)     |
| fish 3.3.0          | `/bin/sh ./script` （補足 D）  |                                 |
| xonsh 0.9.27        | ⚠ `xonsh ./script`             |                                 |
| elvish 0.15.0       | ⚠ エラー（補足 E）             |                                 |
| PowerShell 7.1.3    | ⚠ エラー（補足 E）             |                                 |
| Nushell 0.33.0      | `sh -c ./script`               |                                 |

**補足**

- A: `-` はスクリプト名が `-` で始まる場合にオプションと誤認識されないようにするため
- B: ファイルの一行目が `#` で始まる場合（ただしシバンではないこと）
- C: ファイルの一行目が `:` で始まる場合

D: fish 3.3.0 でシバンや `:` がなくても動くようになった（[参考](https://fishshell.com/docs/current/relnotes.html))

> Shebang (#!) lines are no longer required within shell scripts, improving support for scripts with concatenated binary contents. If a file fails to execute and passes a (rudimentary) binary safety check, fish will re-invoke it using /bin/sh (#7802).

<details>
<summary>E: エラーメッセージ</summary>
<div>
fish 3.2.2

```
Failed to execute process './script'. Reason:
exec: Exec format error
The file './script' is marked as an executable
but could not be run by the operating system.
```

elvish 0.15.0

```
Exception: fork/exec ./script: exec format error
[tty 2], line 1: ./script
```

PowerShell (pwsh) 7.1.3

```
ResourceUnavailable: Program 'script' failed to run: Exec format errorAt line:1 char:1
+ ./script
+ ~~~~~~~~.
```
</div>
</details>

## 補足

### シバンを書かなくてもいい場合

`.bashrc` や `.bash_profile` などのシェルに読み込まれるスクリプトはシバンを書く必要はありません。これらは現在のシェルの設定を変更するものなので現在のシェルに読み込まれるのは当然だからです。シバンを書かなくていいし実行権限も付ける必要はありません。（書いてもコメントとして無視されるので実害はありません。）

### `sh` で実行されないシェルが POSIX 違反なだけじゃないの？

いいえ。POSIX は「POSIX シェル以外を作ってはならない」とは言っていません。新しいソフトウェアを作ってはならないなどと、未来を潰すようなことを言うはずがありません。POSIX にどのようなことが書かれていたとしても、新しいソフトウェアがその範囲で作らければいけないなどということはなく、POSIX 自身もそのような制限をしていません（ちゃんと調べて POSIX の趣旨を理解してください）。

したがってどのような挙動をしていたとしても POSIX 違反にはなりませんし、現実としてシバンがないスクリプトを `sh` で動かさないシェルが複数あるのですから、それらも考慮する必要があります。考慮すればシバンがあったほうが可搬性があるという結論になります。

### `sh` が `/bin/sh` にない環境（Android など）ではどうするの？

POSIX ではインストール時にシバンを書き換えることを推奨しています。その他 Android のような `/bin/sh` が存在してない環境であればシンボリックリンクを貼ることでも対応できます。

参考 [POSIX の sh の APPLICATION USAGE](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html#tag_20_117_16) より

> Furthermore, on systems that support executable scripts (the "#!" construct), it is recommended that applications using executable scripts install them using getconf PATH to determine the shell pathname and update the "#!" script appropriately as it is being installed (for example, with sed). For example:

## まとめ

シバンがないスクリプトを実行した時の挙動は、現在使用しているシェルによって以下のいずれかになります。

1. `/bin/sh` で実行する
2. （`PATH` から見つかった）`sh` で実行する
3. 現在実行中のシェルと同じシェルの別プロセスで実行する<br>**シェルは POSIX シェルとは限りません**
4. 実行ファイルではないというエラーになる

POSIX 標準規格の世界には POSIX シェルしか存在していません。したがってユーザーが使うシェルは POSIX シェルだという前提ですが、それは現実の世界（= 環境）ではありません。現実世界には POSIX シェルではないシェルもたくさんあります。POSIX シェルを使うという前提ではいけません。シバンのないシェルスクリプトを実行したときにエラーになるシェルは実際にあります。シバンを書かない方がいい理由はないのでちゃんと書いておきましょう。