ターミナルから起動するプログラムについて

catプログラムを起動する

例えば、ファイルの中身を見るcatというプログラムを起動してみよう。

ファイル名を引数にとって、ファイルの内容を出力する。

$ cat hello.txt
hello, world!!

プログラムの場所

プログラムはどこかになくてはならない。

catはどこにあるのだろうか。

whichプログラムで確認できる。

$ which cat
/bin/cat

/bin/catにあるらしい。

プログラムの種類

どういうファイルなのだろうか。

fileプログラムでファイルの概要はわかる。

$ file /bin/cat
/bin/cat: Mach-O 64-bit executable x86_64

コンパイルされたバイナリファイルのようだ。

内容は人間が読んで簡単にわかるものではない。

バイナリファイルというのはこのコンピュータのために作られた、

このコンピュータでしか動かないファイルだ。

だからこそ、このコンピュータは実行のしかたを知っている。

しかし、例えばこの/bin/catファイルをWindowsにコピーして実行してみよう。

Windowsは実行のしかたを知らないのでエラーが出る。

次いでpipプログラムをみてみよう。

$ which pip
$ file /usr/local/bin/pip # file `which pip`でも同じ
/usr/local/bin/pip: a /usr/local/opt/python/bin/pytho script text executable

script textと表示された。

テキストとバイナリ

pipの中身をみてみよう。

$ head `which pip`
#!/usr/local/opt/python/bin/python2.7
# EASY-INSTALL-ENTRY-SCRIPT: 'pip==7.1.2','console_scripts','pip'
__requires__ = 'pip==7.1.2'
import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
    sys.exit(
        load_entry_point('pip==7.1.2', 'console_scripts', 'pip')()
    )

ただのテキストファイルだった。

さきほどのcatのように、このコンピュータ用に作られたバイナリファイルではない。

この内容は人間には読めるけれど、コンピュータにはわからないだろう。

ではpipプログラムはなぜ動くのだろう?

答えは、テキストファイルの一行目で、

このテキストファイルを解釈できるプログラムを指定しているからだ。

#!/usr/local/opt/python/bin/python2.7

「このテキストはpython2.7というプログラムが解釈し実行するものだ」ということだ。

なのでpipが実行されるとき、実際には以下のコマンドが実行されているのだ。

$ /usr/local/opt/python/bin/python2.7 pip

このpython2.7というファイルは何者だろうか。

$ file /usr/local/opt/python/bin/python2.7
/usr/local/opt/python/bin/python2.7: Mach-O universal binary with 2 architectures

2つのアーキテクチャに対応していると書かれているが、

基本はこのコンピュータために作られたバイナリファイルだ。

catとpythonの違いはなんだろうか。

それは、catが与えたファイルの内容をそのまま吐き出すのに対し、

pythonは与えたファイルの内容によって動作が全く異なるということだ。

サブプロセスを立ち上げたり、Webサーバーになったりもする。

これは驚くべきことだ。

このようなバイナリファイルをインタプリタ(解釈器・翻訳者)という。

pipを解釈できるのは何もpythonだけではない。

phpというインタプリタバイナリでも解釈できる。

$ php /usr/local/bin/pip

結果は実行してみてのお楽しみ。

プログラムのパス

catの場所をわざわざwhich catで確認したけど、

どうして/bin/catだけでなくcatでも実行できるのだろうか?

実はcatというコマンドを実行したとき、

あるディレクトリリストを順番に探して、

最初にcatという名前に一致したプログラムが実行される。

/bin/というディレクトリも探すディレクトリリストの中に含まれているのだ。

探すディレクトリはPATHという環境変数に、

":"で区切られて格納されている。

PATHの内容は以下のコマンドで確認できる。

echo $PATH

これでは見にくいので以下のコマンドでディレクトリが1行ずつ表示される。

$ echo $PATH | python -c 'for line in raw_input().split(":"):print(line)'

これで列挙されるディレクトリにcatがなければ見つけてはもらえない。

プログラムをコマンドとして登録する

/Users/chyka/my_programをmy_programだけで起動できるようにしてみる。

2つ方法がある。

方法1:my_programがあるディレクトリをPATHに追加する

以下のコマンドで/Users/chykaディレクトリを検索対象にすることができる。

export PATH="${PATH}:/Users/chyka"

このコマンドの意味はスクリプト言語風に書くと、

PATH = PATH + ":/Users/chyka"

だ。

my_programコマンドが使えるようになっているはずだ。

方法2:検索対象のディレクトリのどれかに追加する

$ echo $PATH | python -c 'for line in raw_input().split(":"):print(line)'
...
/usr/local/bin
...

PATHの中にディレクトリならどこでもいいが、

/usr/local/binというディレクトリが含まれているようだ。

ここにmy_programをコピーするとmy_programコマンドが使えるようになる。

ちなみにシンボリックリンクというものがあり、

わざわざファイルをコピーしなくてもそのディレクトリ以下に存在するかのようにリンクを貼ることができる。

Windowsでいうとショートカットみたいなものだ。

sudo ln -s /Users/chyka/my_program /usr/local/bin/my_program