見出し画像

【駄文】シェバン構文がなぜ機能するのか(勝手な仮説の思考実験中)

授業のネタで資料を朝活で作っていながらふと思いました。シェバン構文(#!)によりUNIX LikeなOSでは(対応してる)任意のスクリプト言語を実行できるという機能を有してるけど、なんでできるの? という話。

前提としてはOSが実行時にどう実行するのかをプログラムのヘッダから確認してる(LinuxでいうところのELFヘッダとか)中で、実行形式が判別できない時の流れとしてシェバン構文があるかを見ている部分ですね。

1. 先頭2バイトが#!の時に、改行コードまでを読み込んで解析してインタプリタと引数を決定
2. それすら無ければ/bin/sh向けのコードとして実行

で、シェバン構文があると判別され、インタプリタが特定されたとして、どうやって呼ばれたインタプリタがソースとなるコードを読み込むのかです。(多分これと思われる)答えに気づけばなんてことは無いのですが、思考実験も重要なのでちょっと試してました。

実はbashの場合、スクリプトがどうもFD 255番で渡されているのです。

#!/bin/bash
ls -l /proc/$$/fd

これに実行権を付けて実行すると、255番でスクリプト自身が開かれてました。

densuke@pi:/tmp$ chmod +x hoge
densuke@pi:/tmp$ ./hoge
total 0
lrwx------ 1 densuke densuke 64  92 05:49 0 -> /dev/pts/0
lrwx------ 1 densuke densuke 64  92 05:49 1 -> /dev/pts/0
lrwx------ 1 densuke densuke 64  92 05:49 2 -> /dev/pts/0
lr-x------ 1 densuke densuke 64  92 05:49 255 -> /tmp/hoge

もしや255番で開かれて渡される? と思ってbashのソースを取得して255(0xff)を検索したのですが、それっぽいものは見つからず。
1秒考えたらこの考え方はなにかおかしいということには気づけます。

・OSから渡されるならbash自身が開かずパイプなどストリーム渡しになってるよね
・実はスクリプトに環境変数でそれっぽい固定の変数で渡されてる?

後者を思い、hogeスクリプトにsetコマンドで変数をまるっと出してgrepしてもそれっぽいものは出ませんでした。

FD255説を意識して、Rubyで同じようにFDの対応データを吐かせてみるように書いてみました。

#!/usr/bin/env ruby

pid = $$

Dir.glob("/proc/#{pid}/fd/*").each do |item|
 puts item
 puts "#{item} -> #{File.realpath(item)}"
end

これはエラーになります、File#realpath や File#readlink は、リンク先ファイルが存在してないと例外を出すからです。例外キャッチでもすれば良いんでしょうがそこが本質ではないので。結局が255があるということは確認できずということで「255で呼ばれるんじゃない?」説は廃棄処分です。

そこで20秒ぐらい悩んだ結果として「あぁ、ARGV[0]で渡されたものを読み込んでるのか」ということで今は停止。どう検証したら良いのかがまだ思いつかないので、答えはあるはずなんだろうけど自身で検証できていないから仮説です。カーネルソースでいうところのfs/binfmt_script.cが該当コードで、それっぽいことをしてるのでまぁあたりかな?程度の駄文になるのでした…

この記事が気に入ったらサポートをしてみませんか?