シェルスクリプトの実行形式（shebang ・実行方法の違い）

シェルスクリプトで当然のようにファイル先頭に`#!/bin/bash`と記述し、
`./ファイル名`で実行し、`#!/bin/bash`の記述がないシェルスクリプトには
`source ./ファイル名`で実行していた。

またシェルスクリプト自体に実行権限を与えていない644の状態であるのも関わらず、
`source ./ファイル名`では実行できた。

「こっちの環境では動くのに、あっちの環境では動かない」という理解のままでは
気持ち悪かったの調べたことをまとめた。

## 同記事を読んで理解できること

①shebangの理解
②sourceコマンドの動き
③どのシェル（カレントシェル or サブシェル）がシェルスクリプトを実行しているんだという認識の大切さ
④シェル変数・エイリアスの設定はサブシェル実行時には引き継がれない


## shebang とは　#!/bin/bash

Linuxでは、ファイルを実行する際には、シェルから実行したいファイル名を指定する。
```
# ./study.sh
```

シェルから実行命令を受けたカーネルは、まず対象ファイルの先頭を確認します。そして`#!`があった場合には、その後ろに書かれたコマンドを実行します。

```:study.sh
1#!/bin/bash
2
3 echo $PATH
```
上の例の場合、以下の順番で実行されます
①シェル「おーいカーネル　study.shを実行してくれー」
②カーネル「了解　ファイルの先頭が`#!`だからその直後のコマンド/bin/bashを実行するぞ」
③カーネル「ファイル指定されてるから`/bin/bash ./study.sh`で実行しとくわ」

つまりshebangを記述することで、いちいちコマンドに/bin/bashと入力しなくても
ファイル名を指定するだけでシェルスクリプトが実行できる。



## `sourceコマンド`では実行できるのに、`./ファイル名`では実行できないのはなぜ？

以下のような簡易なシェルスクリプトを作成し、２つの方法で実行する。
```:study.sh
1 echo $PATH
2 du -h ~ | tail -n 1
```
ペーミッションはデフォルトの644のままのため、実行権は付与されていない
```
# ls -l study.sh
-rw-r--r--. 1 root root 33 10月 29 15:57 study.sh
```


### 2通りの実行方法を試す。
①ファイル名のみを指定して実行　`./test.sh`
③sourceコマンドで実行 `source ./test.sh`
```
# ./study.sh
-bash: ./study.sh: 許可がありません
# source ./study.sh
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
352K	/root
```
ここで疑問
・なぜ６４４の実行権の付与されていないファイルにも関わらず、sourceコマンドでは実行できるのか？

sourceコマンドの動きを確認します。

### sourceコマンドの動き

manコマンドでsourceコマンドを見ていきます

```:man sourceの抜粋
source filename [arguments]
Read and execute commands from filename in the current shell environment and 
return the exit status of the last command executed from filename.  
If filename does  not  contain  a　slash,  
filenames  in PATH are used to find the directory containing filename.
```
`Read and execute commands from filename in the current shell environment`
→sourceコマンドではカレントシェルでシェルスクリプトに書かれた内容を実行します。

つまりsourceコマンドは、指定したファイルの内容をそのままコマンドラインに入力した時と
同じように実行されます。これはシェルスクリプトに書かれた一行一行を今カーソルがある
その場に「流し込む」というイメージ。

上記のイメージが理解できると、sourceコマンドでは、644のシェルスクリプトでも
実行できるのが理解できます。
・実行するシェルはカレントシェルなので、シェルスクリプトの先頭部分のshebangは要らない
・対象ファイルを直接実行するわけではないので、ファイル自体に実行権が不要


## ファイル名のみ指定した実行はサブシェルで実行されるため、エイリアスなどの設定が引き継がれない

前述したようにsourceコマンドでは、カレントシェル内でシェルスクリプト実行します。
反対に、ファイル名のみ指定した実行`./study.sh`では、サブシェルでシェルスクリプトが実行されれます。
サブシェルとは、現在のシェルから新しく起動される子プロセスのシェルのこと。

![subshel.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2935967/e184f74e-1111-3163-71c5-02dc82300022.jpeg)

では実行方法による違いを確認します。

カレントシェルでエイリアスを作成します。

```
[root@localhost prac]# alias lsalf='ls -alF'
[root@localhost prac]# lsalf
合計 12
drwxr-xr-x.  3 root root   48 10月 29 03:09 ./
dr-xr-x---. 23 root root 4096 10月 29 21:38 ../
-rw-r--r--.  1 root root 1806 10月 29 02:49 date.log
-rw-r--r--.  1 root root  111 10月 29 02:55 prac.log
drwxr-xr-x.  2 root root   41 10月 29 04:26 sv/
[root@localhost prac]# 
```

上で作成したエイリアスコマンドを実行するシェルスクリプト`study.sh`を用意。

```:study.sh
1 #!/bin/bash
2 lsalf
```

この状態でsourceコマンドとファイル名指名の2通りで実行結果を確認。

```:sourceコマンドで実行
[root@localhost prac]# source ./study.sh 
合計 16
drwxr-xr-x.  3 root root   64 10月 29 21:42 ./
dr-xr-x---. 23 root root 4096 10月 29 21:38 ../
-rw-r--r--.  1 root root 1806 10月 29 02:49 date.log
-rw-r--r--.  1 root root  111 10月 29 02:55 prac.log
-rw-r--r--.  1 root root   19 10月 29 21:42 study.sh
drwxr-xr-x.  2 root root   41 10月 29 04:26 sv/
```
```:ファイル名で実行
[root@localhost prac]# ./study.sh
./study.sh: 行 2: lsalf: コマンドが見つかりません
```

結果、sourceコマンドでは正しく実行できるが、ファイル名での実行はエラーが表示。
これはファイル名実行では、シェルスクリプトを実行するために、新しくbashがサブシェルとして
起動されているため、カレントシェルで定義したエイリアスやシェル変数が引き継がれないため。


## まとめ

シェルスクリプトの３つの実行方法

①ファイル名指定して実行　`./study.sh`
②シェルの引数として実行　 `/bin/bash study.sh`
→①＆②はサブシェルで実行されるため、エイリアス・シェル変数などの設定は引き継がれないことに注意！
③sourceコマンドで実行 `source ./test.sh`
→カレントシェルでシェルスクリプトが実行されるので現在の環境が引き継がれる

シェルスクリプトは実行する方法によって動作がかわるため、どの方法でシェルスクリプトが
実行されるか意識しておくのが大事。