シェルスクリプトファイルに実行権限を付与する意味は?
実行権限が必要な理由を考えた。
目次
疑問
Linux で、次のようなシェルスクリプトを書いた。
#!/bin/bash
echo 'Hello World'
コレを example
という拡張子なしのファイル名で保存し、ターミナルで次のように実行した。
$ bash ./example
続いて、このファイルに実行権限を付与し、次のように実行してみた。
$ chmod 755 ./example
$ ./example
正しく実行できる。
さらに、このファイルを PATH が通っている ${HOME}/bin/
配下に移動し実行してみる。
$ mv ./example "${HOME}/bin/"
$ example
PATH が通っているので実行できた。
そしたら、今度は実行権限を外してみる。
$ chmod 644 "${HOME}/bin/example"
$ example
-bash: /Users/Neo/bin/example: Permission denied
今度は実行権限がなく動かせなくなってしまった。
でも、
$ bash "${HOME}/bin/example"
と書いたら実行できる。
…はて、ファイルの実行権限とは何なのだろう。
シェルスクリプトの場合は bash
コマンドを使ってやれば、実行権限がなくとも実行できるが、何のために実行権限を付けるのだろう?
ユーザがインタプリタを知らずに実行できるようになる
自分が書いた Bash スクリプトを見ると、
$ chmod 755 ./example && ./example
$ bash ./example
どっちでも実行できるならわざわざ実行権限なんて付与せず bash
コマンドで動かしたらいいじゃーん、と思ってしまいがち。
だが、この場合の問題はそのファイルを本当に bash
コマンドで実行できるのかを事前に知っておかないといけないということ。
example
というファイル名だけでは、その中身が Bash 製なのか Ruby 製なのか、区別が付かない。シェル環境の場合、通常は拡張子を見て実行するソフトを切り替えたりはしないので、その代わりにファイルの1行目にシバン (Shebang) を書いて、インタプリタを知らせるワケだ。
実行権限を付与し、ファイル名のみを指定して実行した場合は、シバンで指定されたインタプリタを自動的に利用してプログラムが動く。これが実行権限の効果である。
バイナリファイルを実行して良いかどうかの目印
コレまでシェルスクリプトを前提に話してきたが、コレがバイナリ形式の実行ファイルだったらどうか。
ユーザが何らかの方法で Read (読み取り) しようとしているのか、それともそのバイナリ自体を実行しようとしているのか、を区別してやる必要がある。Read と Write のパーミッションだけではそれを区別できないために、実行権限が存在するという考え。
また、最近だとついつい自分のローカルマシンで、自分が唯一のユーザであり管理者である、という環境が多いが、管理ユーザと一般ユーザがいるようなサーバを考えてみると、「一般ユーザはこのバイナリを実行するのは許可するけど、ファイルとしての読み出しは禁止する」といった時に、実行権限のみ付与して読み取り権限を外したりして制御できる。ファイルをコピーされてリバースエンジニアリングされたりとか、余計な操作をされないように制御できるワケだ。
今日ではそのような状況が少ないが、そうした制御も出来るようになっているようだ。
インタプリタで実行するスクリプトは読み込み権限と実行権限が必要
前述の参考文献で改めて気付いたが、ファイルの1行目にシバンを書くようなスクリプトファイルの場合、
- シェルが fork して exec する
- 1行目のシバンを読み取り、そのインタプリタに処理を任せる
- ココまでは実行権限によって動作する
- 処理を任されたインタプリタがファイルを読み取り、実行していく
- ココでインタプリタにとって読み取り権限が必要になる
というワケで、読み取り権限と実行権限の両方がないと、スクリプトファイルの場合は実行できない。
$ bash example
と書いた場合は、インタプリタを指定しているので実行権限は要らず、読み取り権限によってインタプリタがファイルを読み込んで実行できるので、読み取り権限だけで動作するというワケ。
腑に落ちた?
なんだかほぼほぼ腑に落ちたような、まだちょっとだけ腑に落ちないような。
自分がシェルスクリプトを作って配布する時は、意図せずうっかり実行されないようにという思いから、実行権限は付与せずに渡して、
$ bash ./example
で動かしてね、とお願いすることが多いのだが、コレが親切なのか分からなくなってきた。
シバンは #!/bin/bash
でちゃんと Bash を指定して書いていることだし、実行権限は付与しておいて
$ ./example
で実行してください、と伝えた方がシンプルなのかもしれない。
どっちが良いのかなー。