回答:
シェバングは、 #!
人間が読み取り可能なインスタンスであるマジックナンバーバイト列からなる0x23 0x21
により使用され、exec()
実行すべきファイルは、スクリプトまたはバイナリであるかどうかを決定するために関数のファミリー。shebangが存在する場合、exec()
代わりにshebangの後に指定された実行可能ファイルを実行します。
これは、質問で示された両方のケースで行われているように、コマンドラインでインタープリターを指定してスクリプトを呼び出すと、コマンドラインでexec()
指定されたインタープリターを実行することを意味し、スクリプトも見ないことに注意してください。
したがって、他の人が指摘したようexec()
に、shebang行で指定されたインタープリターを呼び出す場合、スクリプトにはとして実行可能ビットが設定され、呼び出される必要があります./my_shell_script.sh
。
この動作は、次のスクリプトを使用して簡単に実証できます。
#!/bin/ksh
readlink /proc/$$/exe
説明:
#!/bin/ksh
ksh
インタープリターになるように定義します。
$$
現在のプロセスのPIDを保持します。
/proc/pid/exe
プロセスの実行可能ファイルへのシンボリックリンクです(少なくともLinuxでは、AIXでは、/ proc / $$ / object / a.outは実行可能ファイルへのリンクです)。
readlink
シンボリックリンクの値を出力します。
例:
注意:私は、デフォルトのシェルは、Ubuntuの上で、これを実証しています/bin/sh
ためのシンボリックリンクであるダッシュすなわち/bin/dash
および/bin/ksh
へのシンボリックリンクで/etc/alternatives/ksh
順番にへのシンボリックリンクです、/bin/pdksh
。
$ chmod +x getshell.sh
$ ./getshell.sh
/bin/pdksh
$ bash getshell.sh
/bin/bash
$ sh getshell.sh
/bin/dash
はい、そうです。ところで、それはばかげた質問ではありません。私の答えのリファレンスはこちらです。#!でスクリプトを開始する
シバンまたは「バング」ラインと呼ばれます。
これは、Bashインタープリターへの絶対パスに他なりません。
これは、番号記号と感嘆符文字(#!)で構成され、その後に/ bin / bashなどのインタープリターへのフルパスが続きます。
Linuxでのすべてのスクリプトは、最初の行で指定されたインタープリターを使用して実行されますほとんどすべてのbashスクリプトは、多くの場合、#!/ bin / bashで始まります(Bashが/ binにインストールされていると仮定します)別のシェルで実行される場合。シバンは、ベル研究所でバージョン7 Unixと8の間にデニス・リッチーによって導入されました。その後、BerkeleyのBSDラインにも追加されました。
通訳者ラインを無視する(シバン)
インタープリター行を指定しない場合、デフォルトは通常/ bin / shです。ただし、#!/ bin / bash行を設定することをお勧めします。
#!/usr/bin/perl
#!/usr/local/bin/python
#!/usr/local/bin/ruby
、複数のシステムをサポートするために使用される他の一般的なシェバングエントリは次のように、あなたが使用したいインタプリタを見つけるためにENVを使用することです#!/usr/bin/env perl
#!/usr/bin/env python
env
実際にどちらを好むべきでしょうか?PythonとPerlはをよく使用しますがenv
、シェルスクリプトでは、これはしばしば省略され、シェバンは問題のシェルを指します。
env
$ PATHでプログラムを見つけるのはちょっとしたハックです。名前が示すような環境変数は設定しません。$ PATHは、ユーザーごとに異なる結果になる場合があります。しかし、システム上でスクリプトを変更せずに実行するのに役立ちます。これにより、合理的なperlインタープリターが奇妙な場所に置かれます。
exec
Linuxカーネルのシステムコールは、シバン(#!
)をネイティブに理解します
bashで行う場合:
./something
Linuxでは、これはexec
システムコールをpathで呼び出します./something
。
カーネルの次の行は、httpsexec
://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25に渡されたファイルで呼び出されます。
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
ファイルの最初のバイトを読み取り、それらをと比較します#!
。
比較が真の場合、行の残りはLinuxカーネルによって解析され、最初の引数としてexec
パス/usr/bin/env python
と現在のファイルを使用して別の呼び出しが行われます。
/usr/bin/env python /path/to/script.py
これは#
、コメント文字として使用するスクリプト言語で機能します。
はい、次のようにして無限ループを作成できます。
printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a
Bashはエラーを認識します。
-bash: /a: /a: bad interpreter: Too many levels of symbolic links
#!
たまたま人間が読める形式ですが、必須ではありません。
ファイルが異なるバイトで開始された場合、exec
システムコールは異なるハンドラーを使用します。他の最も重要な組み込みのハンドラは、ELF実行可能ファイルのためのものです:https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305バイトをチェックしている7f 45 4c 46
にも人間であることを起こるいます(読み取り可能.ELF
)。/bin/ls
ELF実行可能ファイルであるの最初の4バイトを読み取って確認します。
head -c 4 "$(which ls)" | hd
出力:
00000000 7f 45 4c 46 |.ELF|
00000004
そのため、カーネルはこれらのバイトを検出すると、ELFファイルを取得し、それをメモリに正しく配置し、それを使用して新しいプロセスを開始します。参照:https : //stackoverflow.com/questions/8352535/how-does-kernel-get-an-executable-binary-file-running-under-linux/31394861#31394861
最後に、このbinfmt_misc
メカニズムを使用して独自のシバンハンドラーを追加できます。たとえば、.jar
ファイルのカスタムハンドラーを追加できます。このメカニズムは、ファイル拡張子によるハンドラーもサポートします。もう1つのアプリケーションは、QEMUを使用して、異なるアーキテクチャの実行可能ファイルを透過的に実行することです。
:私は、POSIXしかし、shebangsを指定しないと思うhttps://unix.stackexchange.com/a/346214/32558実行可能なスクリプトは、システムに何かによってサポートされている場合、それは」根拠のセクションでそれを言及し、および形であるが、起こるかもしれない」。
./something
シェルから実行すると、に完全なパスは渡されませんが、exec
正確に入力されたパスが渡されます。あなたの答えでこれを修正できますか?やるecho "$0"
スクリプトで、あなたは、このような場合は表示されます。
実際、結果としてそれを受け取った場合、shebang行に記載されている実行可能ファイルは単なる実行可能ファイルです。一部のテキストインタープリタを実行可能ファイルとして使用することは理にかなっていますが、必ずしも必要ではありません。明確化とデモンストレーションのために、私はかなり役に立たないテストを行いました:
#!/bin/cat
useless text
more useless text
still more useless text
ファイルにtest.txtという名前を付け、実行可能ビットを設定してchmod u+x test.txt
から「呼び出し」ました./test.txt
。予想どおり、ファイルの内容が出力されます。この場合、catはシェバンラインを無視しません。単にすべての行を出力します。したがって、有用なインタープリターはこのシバン行を無視できるはずです。bash、perl、PHPの場合、これは単なるコメント行です。はい、これらはシェバンラインを無視します。
私が収集したものから、ファイルに実行可能ビットが設定されて呼び出されるたびに、カーネルはファイルヘッダーを分析して処理方法を決定します(私の知る限り、LKMを介してカスタムファイル形式のカスタムハンドラーを追加できます)。ファイルが#!付きのテキストファイルのように見える場合 最初の組み合わせでは、その実行は別の実行可能ファイル(通常は一種のシェル)にディスパッチされます。このパスは、同じ行で、上記のシバンの直後に指定されます。その後、カーネルはシェルを実行し、処理するファイルを渡します。
要するに、どのシェルでスクリプトを呼び出すかは問題ではありません。カーネルはどちらの方法でも実行を適切なシェルにディスパッチします。
bash ./myscript.sh
あり./myscript.sh
ます。
chmod +x my_shell_script.sh ; /path/to/my_shell_script.sh # or ./my_shell_script.sh if you happen to be in its directory