<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)
### 載入順序


## 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...
---