---
# System prepended metadata

title: Shell
tags: [Linux]

---

<style>
  .blue {
    color: blue;
  }
</style>

# Shell

## shortcut

- Bash enables emacs mode in default (you can change to vi mode)
- [emacs mode shortcut](https://gist.github.com/tuxfight3r/60051ac67c5f0445efee)
- emacs, Editor Macros, is a family of text editors.
    - characterize: extensibility

## Interactive shell, Login shell, 環境變數讀取流程

- Linux 中根據不同的情境有不同種類的 shell，在啟動時會讀取不同的設定檔
- 所以不同類型的情境，可能有不一樣的環境變數或是 shell 設定

https://ithelp.ithome.com.tw/articles/10232226
https://blog.csdn.net/Solomon1558/article/details/51763751

### Interactive & Non-Interactive Shell

- **Interactive Shell**
    - 使用者可以即時互動的 shell，也就是平常使用的狀態
        - 從 Console 或 SSH 操作都是 Interactive Shell
    - 存在環境變數 **`PS1`**，代表使用者的提示詞 (Prompt, 例如 `$` 和 `#`)
    - 會輸出使用者的提示字，並等待使用者輸入的指令
        - 使用者可以看到指令的輸出，並在一個指令結束後執行下個指令
    - <font class="blue">啟動時讀取 **bashrc** 設定檔</font> 
- **Non-Interactive Shell**
    - 不需要進行互動的 Shell
        - 不會輸出任何的提示字
        - 也不會等待使用者的輸入
    - 通常被自動化和批次處理的程式使用
        - 透過 `bash -c <command>` 執行的指令
        - Shell Script 執行的指令
        - 由程式或服務 (背景程式) 執行的指令
    - 不會讀取 bashrc 設定檔

### bashrc

- 如果 **shell 是 interactive shell**，會執行 *bashrc*
    - *bashrc* 本身也是一個 script
    - 用來初始化和使用者相關的自定義變數和設定
        - `PS1` 環境變數，代表 shell 的提示字
        - `PATH` 變數及其他環境變數
        - Shell 的 highlight、預設選項、錯誤訊息等
        - 指令別名 (alias)
- 有全域和使用者兩種設定檔
    - **全域**的 bashrc 會作用於系統中的所有使用者
        - 路徑通常是 /etc/bash.bashrc 或是 /etc/bashrc
    - **使用者**的 bashrc 只作用在單一個使用者
        - 檔案在該使用者的 home 目錄底下 (~/.bashrc)
- **bashrc 只有 shell 啟動時會被執行一次**
    - 如果修改 bashrc 的內容，想要馬上啟用新的設定，需要用 `source` 指令讀取新的設定
        ```
        source ~/.bashrc
        ```

### Login and Non-Login

- **Login Shell**
    - 存取 shell 時需要經過完整的登入流程
    - 通常有輸入帳號和密碼時，就是 login shell
        - 從 console 或 SSH 登入時
        - 使用 `su` 指令以其他使用者身分使用 shell 時
    - **例外**: 使用 SSH 遠端執行指令
        ```bash
        ssh user@host <command>
        ```
        - 可以直接在遠端執行指令
        - 需要輸入使用者密碼
        - 但不屬於 login shell
    - <font class="blue">會讀取 **profile** 設定檔</font>
- **Non-login Shell**
    - 不需要登入就可以存取
    - 通常是在已登入的情況下，額外開啟的 shell
        - tmux 開啟 pane
        - 在 GUI 環境下，開啟 terminal 視窗
    - 不讀取 profile 設定檔

### profile

- 如果 shell 是 login shell，會執行 profile
    - profile 本身也是 script
    - 用來**初始化環境變數和設定**，且只在使用者登入時執行
        - 通常用來載入其他的設定檔，會根據實際情況啟動不同檔案
        - 根據實際情況設定一些環境變數，例如 `PATH` 和 `PS1`
        - 判斷使用者身分，設定基本的提示字 (root: `#`, other: `$`)
- 有全域和使用者兩個設定檔
    - 全域的 profile 會作用於系統中的所有使用者
        - 路徑通常是 /etc/profile
    - 使用者的 profile 只作用在單一個使用者
        - 檔案在該使用者的 home 目錄底下 (~/.profile)

### 載入順序

![](https://i.imgur.com/gl0gkke.png)

![](https://i.imgur.com/QunpUp3.png =500x)

## Job management

https://zq99299.github.io/linux-tutorial/tutorial-basis/16/02.html

## Special Env Vars

BASH裡面有一些特別的環境變數，以下幾個是我用過的：

- TZ
- PS1
- HOME
- HOSTNAME
- IFS
- PATH
- LANG
- PWD
- TMPDIR
- LD_LIBRARY_PATH

說明請參考：

https://www.shell-tips.com/bash/environment-variables/#gsc.tab=0

## 特殊符號

### 背景執行 &

```
<command> &
```
- 讓 command 在背景執行
- Command 執行的過程中，使用者可以繼續執行其他指令
- 但還是有 SSH **斷線就停止執行**的問題
    - 無法取代 tmux
    - 但是可以讓 shell 使用較有彈性
    - 參考上述的Job management相關內容

### 指令換行 \

```
<command part1> \
<command part2> \
...
```
- 把一行指令 (包含用其他符號串接的多個指令) 分成多行
- 提升可讀性，或是分段輸入指令
- 舉例來說，下面的兩個寫法有相同的效果
    ```bash
    sudo apt install build-essential vim libopenmpi-dev
    ```
    ```bash
    sudo apt install \
        build-essential vim \
        libopenmpi-dev
    ```

## 執行多個指令

- 這幾個符號可以串接多個指令
- 但是指令之間的輸入和輸出不會直接互相影響

### 執行所有指令 ;

```
command1 ; command2 ...
```
- 不論什麼情況，所有指令都會被執行

### 成功才會往下執行 &&

```
command1 && command2
```
- `command1` **執行成功**，才會執行 `command2`

### 失敗才會往下執行 ||

```
command1 || command2
```
- `command1` **執行失敗**，才會執行 `command2`

## Pipe

- 可以把多個指令互相串接
- 並且把指令的輸出當作下一個指令的輸入

### 標準輸出到檔案 > 或 >>

```bash
command  >  file

# 或是

command  >>  file
```
- 可以把指令的標準輸出，儲存到檔案中
- `>` 會把取代檔案中原本的內容
- `>>` 會把輸出內容接在檔案內容之後

### 標準輸出到標準輸入 |

```bash
command1 | command2
```
- `command1` 的輸出會被當作 `command2` 的輸入

### 檔案到標準輸入 <

```bash
command  <  file
```
- 檔案中的內容會被當作指令的輸入

## stdout 和 stderr

- 程式一般的輸出都屬於 stdout
    - 例如: `printf()`, `console.log()`, `echo` ...
- 輸出錯誤訊息時可能使用其他的 function，讓訊息透過 stderr 顯示到 terminal 上

在 Linux 中使用編號代表不同的 I/O stream
- 0: stdin
- 1: stdout
- 2: stderr

`>` 和 `>>` 預設只會把標準輸出導向到檔案中，如果要把 stderr 的的訊息存在檔案中，需要指定要被重新導向的 I/O

### 指定重新導向的 I/O stream

- 在 `>` 之前加上數字，可以指定要被導向的 stream
    - 1 表示 stdout，所以下面兩行的意義相同
        ```
        command > file.txt

        command 1> file.txt
        ```
- `2>` 表示**重新導向 stderr**
    ```
    command 2> error.txt
    ```
- `1>` 和 `2>` 可以結合使用，**分別儲存正常的輸出**結果和**錯誤訊息**
    ```
    command 1> output.txt 2> error.txt
    ```
- `/dev/null` 是一個特殊的檔案，會無視任何輸入的資料，可以把 stderr 導向該檔案，**隱藏指令的錯誤訊息**
    ```
    command 2> /dev/null
    ```
- stderr 的訊息可以導向到 stdout，該指令的 stdout 就會包含**所有的輸出資訊** (stderr + stdout)，等同一般情況下輸出在**螢幕上的所有內容**
    ```
    command > file 2>&1
    
    # 或是
    
    command > 2>&1 file
    ```

## Problems

- Search which file's content contains string `ubuntu` in /etc
```bash=
#!/bin/bash
echo "Enter directory path:"
read directory
echo "Enter keyword to search for:"
read keyword

# 檢查 directory 是否是目錄
if [ -d "$directory" ]; then
  # -type f: 表示查找的是文件而不是目錄
  # -exec: 讓我們將搜尋出來的結果，使用其他的指令進行後續的處理動作
  # {}: 代表找到的文件
  find "$directory" -type f -exec grep -q "$keyword" {} \;
  
  # 表示有成功找到關鍵字
  if [ $? -eq 0 ]; then
    echo "Files containing 'keyword' in $directory."
    find "$directory" -type f -exec grep -l "$keyword" {} \;
  else 
    echo "No files found containing 'keyword' in $directory."
  fi
else 
  echo "Directory not found: $directory"
fi
```
or
```bash=
#!/bin/bash
echo "Enter directory path:"
read directory
echo "Enter keyword to search for:"
read keyword

# 檢查 directory 是否是目錄
if [ -d "$directory" ]; then
  grep -lr $keyword $directory
else 
  echo "Directory not found: $directory"
fi
```

- Find the largest file in / and its sub directory.
- follow /var/log/dmesg (show the new message on console when it's been written)
- show the remaining disk space size and inode count

### Meeting 提出的問題及建議

- 當初設計時，為什麼要切分成 (non-)login、(non-)iteractive shell
    - 彭：
- 環境變數的繼承：為何有時改了某個shell中的環境變數，卻影響到其他種類的shell？
    - Ans: Linux processes 為樹狀結構，PID 1 之部分性質會繼承給其他process。因此，有時環境變數會被其他shell繼承。
- Shell script 之訓練，建議先閱讀他人腳本。某些腳本寫法上會較嚴謹、繁瑣，因為需支援較舊bash版本等。可多看新奇、特殊的寫法。理解script 在大型程式中發揮的作用（如：與自動化的關係等）
- ubuntu 無法使用 apt update：
    - 安裝 ubuntu 後，使用 `apt update` 命令，出現 `E: Some index files failed to download. They have been ignored, or old ones used instead.` 
    - 先前的情況：
        - 使用 `ubuntu-18.04.6-desktop-amd64.iso` 「不會」遇到此問題
        - 使用 `ubuntu-22.04.2-live-server-amd64.iso` 或 `server-22-amd64.iso` 「會」遇到此問題
    - meeting 當日情況：
        - 嘗試 `ping 1.1.1.1`，可行
        - 嘗試 `apt update` 可行
    - 學長建議：猜測理由，使用top-down或buttom-up測試問題出在哪個環節，如: ping, DNS, root CA...
---