どのシェルインタープリターがシバンなしでスクリプトを実行しますか?


16

アカウントのデフォルトのシェルが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

スクリプトは上部に何を言っていますか?
マイケルホーマー

@MichaelHomer:何もありません。質問を編集して、スクリプトの内容を追加しました。スクリプトには実行権限があります。
7_R3X

現在のシェルを使用する場合. prac002.shは、スクリプトが現在のディレクトリにあると仮定して、ドットソースを使用します。
-thecarpy

1
実行. ./prac002.shすると、現在のシェル、つまりドット(.)、スペース()の後にスクリプトのパスが続きます。スクリプトのドットソーシングと呼ばれます。;-)
thecarpy

回答:


18

スクリプトは#!、使用するインタープリターを示すシェバン行で始まっていないため、POSIXは次のように述べています。

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

そのフレージングは​​少し曖昧であり、異なるシェルには異なる解釈があります。

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

これらの行をスクリプトに追加することにより、この場合の動作を確認できます。

echo $BASH_VERSION
echo $ZSH_VERSION

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

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

ps -p $$roolsの答えからのコマンドは、シェルスクリプトを実行するために使用されるコマンドに関する有益な情報が表示されます。


しかし、シェバンが指定されていない場合は、デフォルトのシェルを使用します。これが、あなたが常に、シバンを指定しなければならない理由です。
-thecarpy

4
それはそうではなく、それが実際に答えの全ポイントでした。2番目の文は確かに真実です。
マイケルホーマー

あなたは正しい、bashはそれを使用してそれを実行します、私のボックスのほとんどのデフォルトシェルはたまたま.... bash、そこから私の混乱。私だけのデフォルトのシェル、案の定としてのkshでのSolaris 8でテストされ、bashはbashのようにそれを実行します...学んだ何か新しいこと今日、感謝;-)(upvoted!)
thecarpy

ありがとう。execl()シェルスクリプトにシバンが含まれておらず、次のように実行された場合、呼び出しの失敗は発生しscriptnameますか?シェルスクリプトが次のように実行されたとき、それは起こりませんbash scriptnameか?シェルスクリプトにシバンが含まれており、次のように実行された場合、それは起こりませんscriptnameか?
すべてのStackExchange


7

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

その場合に発生するのは、コマンドを実行するために使用されるアプリケーションまたはライブラリ関数、あるいはその両方です。

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

他のほとんどのアプリケーションは、コマンドを実行するときにこれらのいずれかを使用します。それらはの方法によって、例えばシェルを呼び出すsystem("command line")典型的に起動するlibcの関数shそのコマンドラインを解析する(パスはそのようなコンパイル時(で決定することができる/bin/shVS /usr/xpg4/bin/shSolaris上))、またはシェルたinvokeに格納されている$SHELLように、それ自体でviその !コマンド、またはxterm -e 'command line'他の多くのコマンド(のsu user -c代わりにユーザーのログインシェルを呼び出します$SHELL)。

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

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

シェルに関しては、さまざまなバリエーションがあります。bash、AT&T ksh、Bourneシェルは通常exec、をシミュレートした後(execve()エクスポートされていないすべての変数を設定解除し、すべてのclose-on-exec fdsを閉じ、すべてのカスタムトラップを削除し、エイリアス、関数...(モードでbashスクリプトを解釈しshます)。yash(と自分自身が実行されますshようargv[0]にあるのでsh、それを解釈するモード)。

zshpdkshashベースシェルは、典型的に呼び出すsh(コンパイル時に決定されたパス)。

cshしてtcsh(とshいくつかの初期のBSD)、ファイルの最初の文字がある場合#、その後、彼らはそれを解釈するために自分自身を実行し、そしてだろうshそう。それは、シェバン前の時代にさかのぼり、Bourneシェルではなくコメントとしてcsh認識#したため、#cshスクリプトであるというヒントになりました。

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

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

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

いくつかの例:

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

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

いずれにせよ、構文read -pbash-onlyなので、スクリプトが解釈されるようにしますbash(そして、その誤解を招く.sh拡張子を避けます)。bash実行可能ファイルのパスを知っており、次のいずれかを使用します。

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

または、次を使用して$PATHbash実行可能ファイル(bashインストール済みと仮定)のルックアップを試すことができます。

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

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

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

1

#!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 program is: /usr/bin/bash

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


1
なぜそれをダウン票したのですか?説明してください、または役に立たない
...-rools

憎しみ(サイレント匿名のダウンボッター)は嫌いです。私はあなたの答えから何かを学んだので、ここに賛成票があります。:
Mac
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.