web-dev-qa-db-ja.com

シバンなしでスクリプトを実行するシェルインタープリターはどれですか?

アカウントのデフォルトのシェルがzshであると仮定しますが、ターミナルを開いてbashを起動し、prac002.shという名前のスクリプトを実行しました。このシェルインタープリターを使用して、zshまたはbashのスクリプトを実行します。次の例を検討してください。

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % Sudo cat /etc/passwd | grep papagolf
[Sudo] password for papagolf: 
papagolf:x:1000:1001:Rex,,,:/home/papagolf:/usr/bin/zsh
# papagolf's default Shell is zsh

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % bash
# I fired up bash. (See that '%' Prompt in zsh changes to '$' Prompt, indicating bash.)

papagolf@Sierra:~/My Files/My Programs/Learning/Shell$ ./prac002.sh 
Enter username : Rex
Rex
# Which interpreter did it just use?

**編集:**スクリプトの内容は次のとおりです

papagolf@Sierra ~/My Files/My Programs/Learning/Shell % cat ./prac002.sh 
read -p "Enter username : " uname
echo $uname
20
7_R3X

スクリプトは_#!_ Shebang行で始まっていないため、どのインタープリターを使用するかを示しているため、 POSIXはそれを示しています

execl() 関数が、POSIX.1-2008のシステムインターフェースボリュームで定義されている[ENOEXEC]エラーに相当するエラーのために失敗した場合、シェルは、検索の結果のパス名を最初のオペランドとして使用してシェルを呼び出すのと同等のコマンドを実行します。残りの引数は新しいシェルに渡されますが、「新しいシェルの$ 0 "がコマンド名に設定されている可能性があります。実行可能ファイルがテキストファイルでない場合、シェルはこのコマンドの実行をバイパスする可能性があります。この場合、エラーメッセージを書き込み、終了ステータス126を返します。

その言い回しは少しあいまいで、シェルが異なれば解釈も異なります。

この場合、Bashはそれ自体を使用してスクリプトを実行します。一方、代わりにzshから実行した場合、zshは代わりにsh(システムにあるものは何でも)を使用します。

このケースの動作を確認するには、スクリプトに次の行を追加します。

_echo $BASH_VERSION
echo $ZSH_VERSION
_

Bashの最初の行はバージョンを出力し、2番目の行は使用するシェルに関係なく何も言わないことに注意してください。

  • _/bin/sh_がたとえばdashの場合、スクリプトがzshまたはdashから実行されても、どちらの行も何も出力しません。
  • _/bin/sh_がBashへのリンクである場合、すべてのケースで最初の行の出力が表示されます。
  • _/bin/sh_が直接使用しているものとは異なるバージョンのBashである場合、bashから直接およびzshからスクリプトを実行すると、異なる出力が表示されます。

_ps -p $$_ roolsの回答からのコマンド は、シェルがスクリプトの実行に使用したコマンドに関する有用な情報も表示します。

21
Michael Homer

ファイルはシステムによって認識される実行可能ファイルのタイプではないため、そのファイルを実行する権限があると想定すると、execve()システムコールは通常ENOEXEC実行可能ではありません)エラー。

次に何が起こるかは、コマンドを実行するために使用されるアプリケーションやライブラリ関数次第です。

たとえば、シェル、execlp()/execvp() libc関数です。

他のほとんどのアプリケーションは、コマンドを実行するときにこれらのいずれかを使用します。たとえば、system("command line") libc関数を介してシェルを呼び出し、通常はshを呼び出してコマンドラインを解析します(パスはコンパイル時に決定できます(Solarisの/bin/sh/usr/xpg4/bin/shなど))。または、$Shellコマンドと一緒にviのように、!に格納されているシェルを単独で呼び出すか、xterm -e 'command line'と他の多くのコマンドを使用します(su user -cは、$Shellの代わりにユーザーのログインシェルを呼び出します)。

一般に、#で始まらないシバンレスのテキストファイルは、shスクリプトと見なされます。ただし、どのshかは異なります。

execlp()/execvp()execve()ENOEXECを返すと、通常、shが呼び出されます。複数のshを持つシステムの場合、複数の標準に準拠できるため、shは通常、コンパイル時に決定されます(execvp()/execlp()を使用するアプリケーションのshへの異なるパスを参照するコードの異なるblob。たとえば、Solarisでは、/usr/xpg4/bin/sh(標準、POSIX sh)または/bin/sh(Solaris 10以前のBourne Shell(旧式のシェル)、Solaris 11ではksh93)のいずれかになります。

砲弾に関しては、さまざまなバリエーションがあります。 bash、AT&T ksh、Bourne Shellは通常、execve()をシミュレートした後、スクリプト自体を(execを使用していない場合は子プロセスで)解釈します。 exec fds、すべてのカスタムトラップ、エイリアス、関数を削除しました(bashshモードでスクリプトを解釈します)。 yashはそれ自体を実行し(shargv[0]としてshモードで)解釈して解釈します。

zshpdkshashベースのシェルは、通常、sh(コンパイル時に決定されるパス)を呼び出します。

cshtcsh(および初期のBSDのsh)の場合、ファイルの最初の文字が#である場合、それらはそれ自体を実行して解釈し、そうでない場合はshを実行します。 csh#をコメントとして認識したがBourne Shellを認識しなかったシェバン前の時代にさかのぼるので、#はcshスクリプトであるというヒントでした。

fish(少なくともバージョン2.4.0)。execve()が失敗した場合にエラーを返します(スクリプトとして処理することは試みません)。

一部のシェル(bashやAT&T kshなど)は、まずファイルがスクリプトであるかどうかをヒューリスティックに判別しようとします。そのため、最初の数バイトにNUL文字がある場合、一部のシェルはスクリプトの実行を拒否することがあります。

また、execve()がENOEXECで失敗してもファイルにシバン行がある場合、一部のシェルはそのシバン行自体を解釈しようとします。

だからいくつかの例:

  • $Shell/bin/bashの場合、xterm -e 'myscript with args'myscriptモードでbashによってshによって解釈されます。 xterm -e myscript with argsでは、xtermexecvp()を使用するため、スクリプトはshによって解釈されます。
  • rootのログインシェルがsu -c myscriptで、/bin/shがBourneシェルであるSolaris 10の/bin/shは、myscriptがBourneシェルによって解釈されます。
  • Solaris 10の/usr/xpg4/bin/awk 'BEGIN{system("myscript")'は、/usr/xpg4/bin/shによって解釈されます(/usr/xpg4/bin/env myscriptと同じ)。
  • Solaris 10のfind . -Prune -exec myscript {} \;execvp()を使用)は、POSIX環境でも(準拠バグ)、/bin/shを使用しても/usr/xpg4/bin/findによって解釈されます。
  • csh -c myscriptは、#で始まる場合はcshで解釈され、それ以外の場合はshで解釈されます。

全体として、どのスクリプトがどのように呼び出されるかがわからない場合、そのスクリプトを解釈するためにどのシェルが使用されるかはわかりません。

いずれの場合でも、read -pbash- only構文であるため、スクリプトがbashによって解釈されることを確認する必要があります(そして、誤解を招く.sh拡張を避けてください)。 bash実行可能ファイルのパスがわかっている場合は、次を使用します。

#! /path/to/bash -
read -p ...

または、以下を使用して、bash実行可能ファイルの$PATHルックアップ(bashがインストールされていると想定)に依存して試すこともできます。

#! /usr/bin/env bash
read -p ...

envは、ほぼどこでも/usr/binにあります)。または、POSIX + Bourne互換にすることもできます。その場合は、/bin/shを使用できます。すべてのシステムに/bin/shがあります。それらのほとんどでは、(ほとんどの場合)POSIX互換ですが、代わりにBourne Shellが時々見つかります。

#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"
8

ない場合#!(呼び出されるShebang)行、shが使用されます。これを確認するには、次のスクリプトを実行します。

ps -p $$
echo -n "The real Shell is: "
realpath /proc/$$/exe

私のコンピューターでは

  PID TTY          TIME CMD
13718 pts/16   00:00:00 sh
The real Shell is: /usr/bin/bash

デフォルトのシェルがzshであっても。私のマシンではshコマンドがbashによって実装されているため、これはbashを使用します。

3
rools