shell 變數與環境變數
Tags: linux, zsh, bash, environment
Published: 2023-06-06 14:57:14

在 shell 中建立的變數可分為 shell 變數和環境變數, 兩者最簡單的區分就是：

- shell 變數：只在當前 shell 下有效, 即使啟動子 shell, 這些變數也不會繼承到子 shell 中。
- 環境 (environment )變數：會在當前 shell 以及子 shell 中生效。

以下我們個別說明。


## shell 變數

首先來看 shell 變數：

```bash
$ SHELL_X=20
$ set | grep SHELL_X
SHELL_X=20
$ env | grep SHELL_X
$ bash
> set | grep SHELL_X
> env | grep SHELL_X
>
```

建立變數的方式非常簡單, 就是：

```bash
變數名稱=變數內容
```

要記得等號兩邊不要有空白, 變數內容如果有空白, 就要用雙引號括起來。這樣建立起來的變數是 shell 變數, 所以使用 `set` 列出所有變數時, 會看到 `SHELL_X` 出現在清單中, 但是用 `env` 列出環境變數時, 就不會出現了。

## 環境變數

建立環境變數的方式是使用 `export` 指令：

```bash
$ ENV_X=20
$ set | grep ENV_X
ENV_X=20
$ env | grep ENV_X
$ export ENV_X
$ env | grep ENV_X
ENV_X=20
$ bash
> set ENV_X
> set | grep ENV_X
ENV_X=20
_=ENV_X
> env | grep ENV_X
ENV_X=20
>
```

你可以看到使用 `export` 將 `ENV_X` 變成環境變數後, 不會是使用 `set` 還是 `env` 列出變數, 都會看到 `ENV_X`, 而且在啟動的子 shell 中, 仍然可以看到 `ENV_X`。

## 利用設定檔設定環境變數

剛剛設定的環境變數在 shell 結束後就會消失, 如果想要讓 shell 執行起來後就會自動設定特定的環境變數, 在實際解說各種 shell 的設定檔前, 先來說明 shell 的模式：

- login shell：如果需要輸入帳密認證才能啟動的 sehll 就是 login shell, 否則就不是 login shell。例如在當前 shell 下執行 `zsh` 或是 `bash`, 並不需要輸入帳密, 就不是 login shell。
- interactive shell：有配置新的終端機的 shell 稱為 interactive shell, 例如以 shell 直接執行腳本時並不會配置新的終端機, 所以並不是 interactive shell。

是否為 login shell 或 interactive shell 會決定要採用哪些設定檔, 以下針對不同 shell 程式說明。

### [zsh](https://zsh.sourceforge.io/Guide/zshguide.html)

[zsh](https://zsh.sourceforge.io/Guide/zshguide02.html#l9) 的設定檔執行順序如下： 


1. 不論是哪一種模式, 都會執行先執行 `/etc/zshenv` 檔, 例如我們將該檔案內容加上：

    ```bash
    echo "/etc/zshenv read."
    ```

    直接執行 zsh：

    ```bash
    $ zsh
    /etc/zshenv read.
    ```

    或是重新登入：

    ```bash
    /etc/zshenv read.

    $
    ```

    都會看到檔案中的 `echo` 顯示的 "/etc/zshenv read."。

2. 每個 `/etc` 下的設定檔, 都有一個使用者私有的 `.` 版本, 所以每個 zsh 也都會執行在使用者家目錄下的 `.zshenv` 檔。如果在這個檔案裡面加入：

    ```bash
    echo ".zshenv read."
    ```

    不論是直接執行 zsh：

    ```bash
    $ zsh
    /etc/zshenv read.
    .zshenv read.

    $
    ```

    或是重新登入：

    ```bash
    /etc/zshenv read.
    .zshenv read.

    $
    ```

    都可以看到先執行 `/etc/zshenv` 接著執行`.zshenv`。

3. 若是 login shell, 會接著執行 `/etc/zprofile`, 以及 `.zprofile` 試著在該檔中個別加入對應的 `echo` 指令, 可以發現直接執行 `bash` 並不會執行這兩個設定檔：

    ```bash
    $ zsh
    /etc/zshenv read.
    .zshenv read.

    $    
    ```
    
    但如果是重新登入, 因為是 login shell, 所以會執行這兩個設定檔：
    
    ```bash
    /etc/zshenv read.
    .zshenv read.
    /etc/zprofile read.
    .zprofile read.

    $    
    ```
    
4. 接著若是 interactive shell, 就會再執行 `/etc/zshrc` 以及 `.zshrc` 這一對檔案, 我們也利用同樣的方式加入 `echo` 指令測試。直接執行 `zsh` 結果如下：

    ```bash
    $ zsh
    /etc/zshenv read.
    .zshenv read.
    /etc/zshrc read.
    .zshrc read.

    $
    ```

    表示這是一個 interactive shell, 如果改成執行一個 shell 腳本：
    
    ```bash
    $ zsh hello.sh
    /etc/zshenv read.
    .zshenv read.
    hello

    $
    ```

    就不是 interactive shell, 也就不會執行 `/etc/zshrc` 與 `.zshrc` 這對檔案了。

    重新登入則也會是 interactive shell：

    ```bash
    /etc/zshenv read.
    .zshenv read.
    /etc/zprofile read.
    .zprofile read.
    /etc/zshrc read.
    .zshrc read.

    $
    ```
    
5. 如果是 login shell, 還會再執行 `/etc/zlogin` 與 `zlogin` 檔, 透過一樣的方式測試, 若直接執行 `zsh`：

    ```bash
    $ zsh
    /etc/zshenv read.
    .zshenv read.
    /etc/zshrc read.
    .zshrc read.

    $
    ```

    改成重新登入就不一樣了：

    ```bash
    /etc/zshenv read.
    .zshenv read.
    /etc/zprofile read.
    .zprofile read.
    /etc/zshrc read.
    .zshrc read.
    /etc/zlogin read.
    .zlogin read.

    $
    ```

以上就是 zsh 執行個別設定檔的順序。

### [bash](https://www.digitalocean.com/community/tutorials/how-to-read-and-set-environmental-and-shell-variables-on-linux)

[bash](https://www.man7.org/linux/man-pages/man1/bash.1.html#INVOCATION) 和 zsh 不大一樣, 詳細可參考[這個圖解](http://www.solipsys.co.uk/new/BashInitialisationFiles.html), 約略的順序如下：

1. 如果不是 interactive shell, 就會執行 `BASH_ENV` 環境變數指定的檔案, 例如：

    ```bash
    $ set | grep BASH_ENV
    BASH_ENV=.non_interactive_shell
    $ cat .non_interactive_shell
    echo ".non_interactive_shell read."
    $ cat hello.sh
    echo hello
    $ bash hello.sh
    .non_interactive_shell read.
    hello
    $
    ```

    可以看到因為是執行腳本, 所以並不是 interactive shell, 會執行 `BASH_ENV` 環境變數指定的 `.non_interactive_shell` 檔。如果單純執行 `bash`：

    ```bash
    $ bash
    $
    ```

    就不會執行 `BASH_ENV` 環境變數指定的 `.non_interactive_shell` 檔

2. 如果是 interactive shell, 但是 login shell, 就會先執行 `/etc/profile`, 然後再執行 `.bash_profile`、`.bash_login`、`.profile` 中第一個出現的檔案。

3. 如果是 interactive shell 也不是 login shell, 就會執行 `/etc/bash.bashrc` 檔。

4. 只要是 interactive shell, 最後還會執行 `.bashrc` 檔。