唯一の大きな違いは、スクリプトのソースと実行の違いです。source foo.sh
それをソースし、あなたが示す他のすべての例が実行されます。さらに詳細に:
./file.sh
これfile.sh
により、現在のディレクトリにあるというスクリプトが実行されます(./
)。通常、実行するcommand
と、シェルは$PATH
と呼ばれる実行可能ファイルを探してディレクトリを検索しますcommand
。/usr/bin/command
またはなどのフルパスを指定すると./command
、$PATH
は無視され、その特定のファイルが実行されます。
../file.sh
これは./file.sh
、の現在のディレクトリを検索する代わりfile.sh
に、親ディレクトリ(../
)を検索することを除いて、基本的に同じです。
sh file.sh
これsh ./file.sh
は、上記と同様file.sh
、現在のディレクトリで呼び出されるスクリプトを実行します。違いは、sh
シェルで明示的に実行していることです。Ubuntuシステムではdash
、そうではありませんbash
。通常、スクリプトには、実行するプログラムを提供するシェバン行があります。別のものでそれらを呼び出すことはそれをオーバーライドします。例えば:
$ cat foo.sh
#!/bin/bash
## The above is the shebang line, it points to bash
ps h -p $$ -o args='' | cut -f1 -d' ' ## This will print the name of the shell
このスクリプトは、実行に使用されたシェルの名前を単に出力します。さまざまな方法で呼び出されたときに返されるものを見てみましょう:
$ bash foo.sh
bash
$ sh foo.sh
sh
$ zsh foo.sh
zsh
そのため、スクリプトを呼び出して呼び出すshell script
と、shebang行(存在する場合)がオーバーライドされ、指定したシェルでスクリプトが実行されます。
source file.sh
または . file.sh
これは、驚くべきことに、スクリプトのソースと呼ばれます。キーワードsource
は、シェルの組み込み.
コマンドのエイリアスです。これは、現在のシェル内でスクリプトを実行する方法です。通常、スクリプトが実行されると、現在のシェルとは異なる独自のシェルで実行されます。説明する:
$ cat foo.sh
#!/bin/bash
foo="Script"
echo "Foo (script) is $foo"
ここfoo
で、親シェルで変数を別の値にfoo
設定してからスクリプトを実行すると、スクリプトは異なる値を出力します(スクリプト内でも設定されているため)がfoo
、親シェルの値は変更されません:
$ foo="Parent"
$ bash foo.sh
Foo (script) is Script ## This is the value from the script's shell
$ echo "$foo"
Parent ## The value in the parent shell is unchanged
ただし、スクリプトを実行する代わりにソースする場合、同じシェルで実行されるためfoo
、親の値が変更されます。
$ source ./foo.sh
Foo (script) is Script ## The script's foo
$ echo "$foo"
Script ## Because the script was sourced,
## the value in the parent shell has changed
したがって、スクリプトは、実行元のシェルに影響を与えるいくつかのケースで使用されます。通常、シェル変数を定義し、スクリプトの終了後に使用可能にするために使用されます。
これらすべてを念頭に置いて、さまざまな答えを得る理由は、まず、スクリプトが思ったとおりに動作しないからです。bash
の出力に現れる回数をカウントしps
ます。これは、開いている端末の数ではなく、実行中のシェルの数です(実際、それでもありませんが、それは別の議論です)。明確にするために、スクリプトをこれに少し簡略化しました。
#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo "The number of shells opened by $logname is $not"
そして、単一のターミナルのみを開いて、さまざまな方法で実行します。
直接起動、./foo.sh
。
$ ./foo.sh
The number of shells opened by terdon is 1
ここでは、シバンラインを使用しています。これは、そこに設定されているものによってスクリプトが直接実行されることを意味します。これは、スクリプトがの出力に表示される方法に影響しps
ます。としてリストされるのbash foo.sh
ではなく、として表示されるだけなのでfoo.sh
、grep
見逃すことになります。実際には、3つのbashインスタンスが実行されています。親プロセス、スクリプトをps
実行するbash、およびコマンドを実行する別のインスタンスです。これは重要です。コマンド置換(`command`
または$(command)
)を使用してコマンドを起動すると、親シェルのコピーが起動され、コマンドが実行されます。ただし、ここでは、ps
出力を表示する方法のため、これらのいずれも表示されません。
明示的な(bash)シェルを使用した直接起動
$ bash foo.sh
The number of shells opened by terdon is 3
ここでは、で実行しているためbash foo.sh
、の出力ps
が表示されbash foo.sh
、カウントされます。したがって、ここには親プロセス、bash
スクリプトの実行、およびクローンシェル(実行ps
)がすべて表示されps
ますbash
。これは、コマンドに単語が含まれるため、それぞれが表示されるためです。
別のシェルでの直接起動(sh
)
$ sh foo.sh
The number of shells opened by terdon is 1
これは、を使用してスクリプトを実行しているため、ではsh
ありませんbash
。したがって、唯一のbash
インスタンスは、スクリプトを起動した親シェルです。上記の他のすべてのシェルは、sh
代わりに実行されています。
ソーシング(.
またはsource
、同じものによる)
$ . ./foo.sh
The number of shells opened by terdon is 2
上で説明したように、スクリプトをソースすると、親プロセスと同じシェルで実行されます。ただし、ps
コマンドを起動するために別のサブシェルが開始され、合計で2つになります。
最後に、実行中のプロセスをカウントする正しい方法は、解析するのではps
なくを使用することpgrep
です。これらの問題はすべて、実行するだけで回避できます。
pgrep -cu terdon bash
したがって、常に正しい番号を出力するスクリプトの作業バージョンは次のとおりです(コマンドの置換がないことに注意してください)。
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
他のすべての起動方法では、ソースを取得すると1が返され、スクリプトを実行するために新しいbashが起動されるため2が返されます。sh
子プロセスはでないため、起動時に1を返しbash
ます。