# 【Treeman】Linux

# shell

# 【jq】 linux json 查詢工具(查詢篇)

- data

```JSON
{
  "firstName": "John",
  "lastName": "Smith",
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021"
  },
  "phoneNumber": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "fax",
      "number": "646 555-4567"
    }
  ],
  "gender": {
    "type": "male"
  }
}
```

- 取得資料(age)

```shell
[root@jylee-stres-stest250 tmp]# cat data.json | jq .age

25
```

- 取得資料(city) (addres &gt; city)

```shell
[root@jylee-stres-stest250 tmp]# cat data.json | jq .address.city
"New York"
```

- 輸出不帶雙引號 <span style="color: #ff0000;">-r</span> :

```shell
[root@jylee-stres-stest250 tmp]# cat data.json | jq .firstName
"John"

[root@jylee-stres-stest250 tmp]# cat data.json | jq -r .firstName
John
```

- 取得資料(city) (addres &gt; city)

```shell
[root@jylee-stres-stest250 tmp]# cat data.json | jq .address.city
"New York"
```

- 取得電話

```shell
# 取得電話陣列
[root@jylee-stres-stest250 tmp]# cat data.json | jq .phoneNumber
[
  {
    "type": "home",
    "number": "212 555-1234"
  },
  {
    "type": "fax",
    "number": "646 555-4567"
  }
]



#取得第一組電話
[root@jylee-stres-stest250 tmp]# cat data.json | jq .phoneNumber[0]
{
  "type": "home",
  "number": "212 555-1234"
}

#取得第一組電話( type + number )
[root@jylee-stres-stest250 tmp]#cat data.json | jq -r '.phoneNumber[0] | .type+","+.number'
home,212 555-1234

#取得所有type
[root@jylee-stres-stest250 tmp]# cat data.json | jq '.phoneNumber[] | .type'
"home"
"fax"
```

- 使用map,join重組資料

```shell
[root@jylee-stres-stest250 tmp]# cat data.json | jq '.phoneNumber | map(.type)' 
[
  "home",
  "fax"
]
[root@jylee-stres-stest250 tmp]# cat data.json | jq '.phoneNumber | map(.type) | join(",")' 
"home,fax"
```

- 使用select 過濾資料

```shell
[root@jylee-stres-stest250 tmp]# cat sample.json | jq -r '.phoneNumber[] | select(.type == "home") '
{
  "type": "home",
  "number": "212 555-1234"
}

[root@jylee-stres-stest250 tmp]# cat sample.json | jq -r '.phoneNumber[] | select(.type == "home") | .number'
212 555-1234
```

- 依key取出值

```shell
# 回傳物件
[root@jylee-stres-stest250 tmp]# echo '{"a":1, "ip":["10.10.10.1","10.10.10.2"], "c":3, "d":4}' | jq '{ip, d}'

{
  "ip": [
    "10.10.10.1",
    "10.10.10.2"
  ],
  "d": 4
}

# 回傳傳列
[root@jylee-stres-stest250 tmp]# echo '{"a":1, "ip":["10.10.10.1","10.10.10.2"], "c":3, "d":4}' | jq '[{ip, d}]'

[
  {
    "ip": [
      "10.10.10.1",
      "10.10.10.2"
    ],
    "d": 4
  }
]
```

```shell
# 綜合查詢
# @timestamp > 2025-08-20T16:, http_status 5開頭
jq -r 'select((.["@timestamp"] | startswith("2025-08-20T16:"))
              and ((.http_status // .status | tostring) | test("^5")))
       | [."@timestamp", (.http_status // .status | tostring), .request_uri] | @tsv' \
  access.log-20250821
  
2025-08-20T16:15:59+08:00	500	/search/searchShop.jsp?keyword=%E6%A5%B5%E8%87%B4%E9%A2%A8%E6%A0%BC%20%E7%9A%AE%E8%B3%AA%E5%AE%A4%E5%85%A7%E6%8B%96%E9%9E%8B
2025-08-20T16:15:59+08:00	500	/search/_next/static/css/ed87e7f553f8bcd9.css
2025-08-20T16:16:00+08:00	500	/search/searchShop.jsp?keyword=%E5%B9%B3%E6%9D%BF%E9%9B%BB%E8%85%A6
2025-08-20T16:16:00+08:00	500	/search/_next/static/chunks/393-eda0e0e00dcf367e.js
2025-08-20T16:16:00+08:00	500	/search/_next/static/media/momo-icon.42337f3f.ttf
2025-08-20T16:16:00+08:00	500	/search/_next/static/chunks/webpack-b9b83558616d5724.js
2025-08-20T16:16:00+08:00	500	/search/_next/static/chunks/main-app-553b0eca9bdb3ac5.js
```

## jq\_array\_to\_shell

---

data

```shell
{
  "values": [
        {
          "email": "user1@domain.com",
          "id": "USER1_ID"
        },{
          "email": "user2@domain.com",
          "id": "USER2_ID"
        }
	]
}
```

ss.sh

```shell
while IFS=',' read -r email id; do
  echo "$email"
  echo "$id"
done <<EOF
$(jq -r '.values[] | "\(.email),\(.id)"' data2.json)
EOF
```

```shell
[root@jylee-stres-stest250 tmp]# sh ss.sh
user1@domain.com
USER1_ID
user2@domain.com
USER2_ID
```

[https://stedolan.github.io/jq/manual/#Invokingjq](https://stedolan.github.io/jq/manual/#Invokingjq)

# 【jq】 linux json 查詢工具(修改篇)

- 修改前節點資料

```shell
[root@jylee-stres-stest250 tmp]# echo '{"a":{"b":0, "c":1}}' | jq .
{
  "a": {
    "b": 0,
    "c": 1
  }
}
```

- 新增 / 修改 : setpath(\[`路徑`\];`值`)

```shell
[root@jylee-stres-stest250 tmp]# echo '{"a":{"b":0, "c":1}}' | jq 'setpath(["a","b"];1)'
#修改 a . b ; 0 => 1
{
  "a": {
    "b": 1,
    "c": 1
  }
}

#修改 多筆值用 | 分開
#修改 a . b ; 0 => 1,
# 修改 a . c ;1=> array( 1,2)
[root@jylee-stres-stest250 tmp]# echo '{"a":{"b":0, "c":1}}' | jq 'setpath(["a","b"];1) | setpath(["a","c"];[1,2])'
{
  "a": {
    "b": 1,
    "c": [
      1,
      2
    ]
  }
}
```

- 刪除 delpath`s`(\[ \[`刪除節點1`\] ,\[`刪除節點2`\]...\])

```
[root@jylee-stres-stest250 tmp]# echo '{"a":{"b":0, "c":1},"d": 111}' | jq .
{
  "a": {
    "b": 0,
    "c": 1
  },
  "d": 111
}

# 刪除d節點
[root@jylee-stres-stest250 tmp]# echo '{"a":{"b":0, "c":1},"d": 111}' | jq 'delpaths([["d"]])'
{
  "a": {
    "b": 0,
    "c": 1
  }
}

# 刪除d節點, a.b 節點
[root@jylee-stres-stest250 tmp]# echo '{"a":{"b":0, "c":1},"d": 111}' | jq 'delpaths([["d"],["a","b"]])'
{
  "a": {
    "c": 1
  }
}
```

# 【jq】相關連結

- [jq play](https://jqplay.org/)
- [jq 常用操作 - mozillazg's Blog](https://mozillazg.com/2018/01/jq-use-examples-cookbook.html)

# 【Shell】【tip】 找出目前誰使用swap

who\_use\_swap.sh

```shell

#!/bin/bash
for p in /proc/[0-9]*; do
  pid=${p##*/}
  swap=$(awk '/VmSwap:/ {print $2}' $p/status 2>/dev/null)
  comm=$(tr -d '\0' < $p/comm 2>/dev/null)
  [ -n "$swap" ] && [ "$swap" -gt 0 ] && printf "%10s KiB  pid=%s  %s\n" "$swap" "$pid" "$comm"
done | sort -nr | head -20
```

```
[root@localhost shell]# bash who_use_swap.sh 
       508 KiB  pid=31107  java
       204 KiB  pid=16675  java
       136 KiB  pid=30500  sentry
       116 KiB  pid=14021  sentry
       104 KiB  pid=1040  containerd
        88 KiB  pid=30650  sentry
        72 KiB  pid=30508  sentry
        52 KiB  pid=29817  sentry
        52 KiB  pid=10666  dockerd
        44 KiB  pid=30521  sentry
        40 KiB  pid=30491  sentry
        40 KiB  pid=12964  sentry
        36 KiB  pid=338  sentry
        28 KiB  pid=30082  sentry
        24 KiB  pid=30615  sentry
        20 KiB  pid=322  sentry
        16 KiB  pid=32662  containerd-shim
        16 KiB  pid=31934  sentry
        16 KiB  pid=30911  sentry
        16 KiB  pid=26832  sentry

```

# 【Shell】【tip】使用暫存表，更新crontab

```shell
#!/bin/bash
# $$ => pid，使暫存檔名不重複
tmp_file=/tmp/cron$$

# 利用trap 刪除暫存檔
#This installs a trap handler for exit (0) and signals 1 (SIGHUP), 3 (SIGQUIT) and 15 (SIGTERM) 
# that removes a file presumably storing a process ID.
#See man 3 signal for details about signals.
trap "rm -rf $tmp_file" 0 1 3 15


cat > $tmp_file << EOF 
*/10 * * * * echo "Hello World"
EOF

cat $tmp_file
cat $tmp_file | crontab
```

# 【Shell】【tip】使用 echo 輸出排程時間到log

```shell
# 使用 echo 輸出排程時間到log 
# 測試輸出到 /tmp/crontab.log
0 8 * * *  echo "$(date '+\%Y-\%m-\%d \%H:\%M:\%S') - $(curl -s 'http://localhost:8080/api/schedule/sendSMS/sendDailySms' 
--form "date=$(date -d 'yesterday' +\%Y-\%m-\%d)" --form 'onlyCheckData=true')" 
>> /tmp/crontab_$(date '+\%Y-\%m').log
```

# 【Shell】linux 下source、sh、bash、./執行指令碼的區別

[linux 下source、sh、bash、./執行指令碼的區別 - IT閱讀 (itread01.com)](https://www.itread01.com/content/1548311242.html)

<div id="bkmrk-1%E3%80%81source%E5%91%BD%E4%BB%A4%E7%94%A8%E6%B3%95%EF%BC%9A-%E3%80%80%E3%80%80sour"><div><div>1、source命令用法：</div><div> source FileName</div><div> 作用:在當前bash環境下讀取並執行FileName中的命令。該filename檔案可以無"執行許可權"</div><div> 注：該命令通常用命令“.”來替代。</div><div> 如：source .bash_profile</div><div> . .bash_profile兩者等效。</div><div> source(或點)命令通常用於重新執行剛修改的初始化文件。</div><div> source命令(從 C Shell 而來)是bash shell的內建命令。</div><div> 點命令，就是個點符號，(從Bourne Shell而來)。</div><div> source的程式主體是bash，指令碼中的$0變數的值是bash，而且由於作用於當前bash環境，指令碼中set的變數將直接起效  
 </div><div>2、 sh, bash的命令用法：</div><div> sh/bash FileName</div><div> 作用:開啟一個子shell來讀取並執行FileName中命令。該filename檔案可以無"執行許可權"</div><div> 注：執行一個shell指令碼時會啟動另一個命令直譯器.</div><div> 每個shell指令碼有效地執行在父shell(parent shell)的一個子程序裡.</div><div> 這個父shell是指在一個控制終端或在一個xterm視窗中給你命令指示符的程序.</div><div> shell指令碼也可以啟動他自已的子程序.</div><div> 這些子shell(即子程序)使指令碼並行地，有效率地地同時執行指令碼內的多個子任務.</div><div> 在ubuntu中sh只是bash的一個連結。</div><div> 由於是在子shell中執行，指令碼設定的變數不會影響當前shell。  
 </div><div>3、./的命令用法：</div><div> ./FileName</div><div> 作用:開啟一個子shell來讀取並執行FileName中命令。該filename檔案需要"執行許可權"</div><div> 注：執行一個shell指令碼時會啟動另一個命令直譯器.</div><div> 每個shell指令碼有效地執行在父shell(parent shell)的一個子程序裡.</div><div> 這個父shell是指在一個控制終端或在一個xterm視窗中給你命令指示符的程序.</div><div> shell指令碼也可以啟動他自已的子程序.</div><div> 這些子shell(即子程序)使指令碼並行地，有效率地地同時執行指令碼內的多個子任務.</div><div> 由於是在子shell中執行，指令碼設定的變數不會影響當前shell。</div><div>exec和source都屬於bash內部命令（builtins commands），在bash下輸入man exec或man source可以檢視所有的內部命令資訊。</div></div></div>bash shell的命令分為兩類：外部命令和內部命令。外部命令是通過系統呼叫或獨立的程式實現的，如sed、awk等等。內部命令是由特殊的檔案格式（.def）所實現，如cd、history、exec等等。

在說明exe和source的區別之前，先說明一下fork的概念。

fork是linux的系統呼叫，用來建立子程序（child process）。子程序是父程序(parent process)的一個副本，從父程序那裡獲得一定的資源分配以及繼承父程序的環境。子程序與父程序唯一不同的地方在於pid（process id）。

環境變數（傳給子程序的變數，遺傳性是本地變數和環境變數的根本區別）只能單向從父程序傳給子程序。不管子程序的環境變數如何變化，都不會影響父程序的環境變數。

<div id="bkmrk-"><div><div></div></div></div>shell script:

<div id="bkmrk-%E6%9C%89%E5%85%A9%E7%A8%AE%E6%96%B9%E6%B3%95%E5%9F%B7%E8%A1%8Cshell%C2%A0scripts"><div><div>有兩種方法執行shell scripts，一種是新產生一個shell，然後執行相應的shell scripts；一種是在當前shell下執行，不再啟用其他shell。  
新產生一個shell然後再執行scripts的方法是在scripts檔案開頭加入以下語句  
\#!/bin/sh  
一般的script檔案(.sh)即是這種用法。這種方法先啟用新的sub-shell（新的子程序）,然後在其下執行命令。  
另外一種方法就是上面說過的source命令，不再產生新的shell，而在當前shell下執行一切命令。</div></div></div>source:

source命令即點(.)命令。

在bash下輸入man source，找到source命令解釋處，可以看到解釋"Read and execute commands from filename in the current shell environment and ..."。從中可以知道，source命令是在當前程序中執行引數檔案中的各個命令，而不是另起子程序(或sub-shell)。

<div id="bkmrk--0"><div><div></div></div></div>exec:

在bash下輸入man exec，找到exec命令解釋處，可以看到有"No new process is created."這樣的解釋，這就是說exec命令不產生新的子程序。那麼exec與source的區別是什麼呢？

<div id="bkmrk-exec%E5%91%BD%E4%BB%A4%E5%9C%A8%E5%9F%B7%E8%A1%8C%E6%99%82%E6%9C%83%E6%8A%8A%E7%95%B6%E5%89%8D%E7%9A%84shell"><div><div>**exec命令在執行時會把當前的shell process關閉，然後換到後面的命令繼續執行。**  
  
</div></div></div>\* fork ( /directory/script.sh)  
 fork是最普通的, 就是直接在腳本里面用/directory/script.sh來呼叫script.sh這個指令碼.執行的時候開一個sub-shell執行呼叫的指令碼，sub-shell執行的時候, parent-shell還在。sub-shell執行完畢後返回parent-shell. sub-shell從parent-shell繼承環境變數.但是sub-shell中的環境變數不會帶回parent-shell  
\* source (source /directory/script.sh)  
 與fork的區別是不新開一個sub-shell來執行被呼叫的指令碼，而是在同一個shell中執行. 所以被呼叫的指令碼中宣告的變數和環境變數, 都可以在主指令碼中得到和使用.  
\* exec (exec /directory/script.sh)  
 exec與fork不同，不需要新開一個sub-shell來執行被呼叫的指令碼. 被呼叫的指令碼與父指令碼在同一個shell內執行。但是使用exec呼叫一個新指令碼以後, 父指令碼中exec行之後的內容就不會再執行了。這是exec和source的區別。

下面用一個例子來講解

1.sh

```shell
#!/bin/bash  

A=B  

echo "PID for 1.sh before exec/source/fork:$"  
export A  

echo "1.sh: \$A is $A"  

case $1 in  
        exec)  
                echo "using exec..."  
                exec ./2.sh ;;  
        source)  
                echo "using source..."  
                . ./2.sh ;;  
        *)  
                echo "using fork by default..."  
                ./2.sh ;;  
esac  

echo "PID for 1.sh after exec/source/fork:$"  
echo "1.sh: \$A is $A"  
```

2.sh

```shell

#!/bin/bash  

echo "PID for 2.sh: $"  
echo "2.sh get \$A=$A from 1.sh"  

A=C  

export A  

echo "2.sh: \$A is $A"  
```

<div id="bkmrk-%E4%B8%8B%E9%9D%A2%E5%9C%A8%E5%91%BD%E4%BB%A4%E5%88%97%E4%B8%AD%E5%8E%BB%E5%9F%B7%E8%A1%8C"><div>下面在命令列中去執行</div></div>./1.sh fork

[![image-1645383274668.png](https://bookstack.treemanou.com/uploads/images/gallery/2022-02/scaled-1680-/YdsokHYuj7W3z5Ib-image-1645383274668.png)](https://bookstack.treemanou.com/uploads/images/gallery/2022-02/YdsokHYuj7W3z5Ib-image-1645383274668.png)

可以看到，1.sh是在父程序中執行，2.sh是在子程序中執行的，父程序的PID是5344，而子程序的是5345，當子程序執行完畢後，控制權返回到父程序。同時，在子程序改變環境變數A的值不會影響到父程序。

./1.sh source

[![image-1645383287482.png](https://bookstack.treemanou.com/uploads/images/gallery/2022-02/scaled-1680-/tOSV4Em25BecrFxb-image-1645383287482.png)](https://bookstack.treemanou.com/uploads/images/gallery/2022-02/tOSV4Em25BecrFxb-image-1645383287482.png)

由結果可知，1.sh和2.sh都是在同一程序中執行的，PID為5367

./1.sh exec

[![image-1645383299760.png](https://bookstack.treemanou.com/uploads/images/gallery/2022-02/scaled-1680-/tvf4pSNRddzmZj05-image-1645383299760.png)](https://bookstack.treemanou.com/uploads/images/gallery/2022-02/tvf4pSNRddzmZj05-image-1645383299760.png)

可知，兩個指令碼都是在同一程序中執行，但是請注意，使用exec終止了原來的父程序，因此，可以看到

```shell
echo "PID for 1.sh after exec/source/fork:$"  

echo "1.sh: \$A is $A"  
```

這兩個命令沒有執行

由這個例子，便大致可瞭解它們的區別了

<center id="bkmrk--4"></center>

# 【Shell】date 做 timestamp 格式轉換計算

要將日期與 timestamp 互相做格式轉換，只要使用 date 就可以達成，date 還可以順便做些日期的加減換算。

- 日期轉換成 timestamp 
    - $ date -d "2018-02-08" +%s  
        1518019200
- timestamp 轉換成可識別的時間 
    - $ date --date=@1518019200  
        四 2月 8 00:00:00 CST 2018
    - $ date "+%Y-%m-%d" --date=@1518019200 # 指定格式  
        2018-02-08
    - $ date "+%Y-%m-%d" # 直接秀今天的日期
- 日期加減換算 
    - $ date -d "-5 day" "+%Y-%m-%d" # 5天前的日期

[&lt;來源&gt;Linux 使用 date 做 timestamp 格式轉換計算 – Tsung's Blog (longwin.com.tw)](https://blog.longwin.com.tw/2018/02/linux-shell-date-timestamp-convert-2018/)

# 【Shell】awk 取出特定欄位

- AWK 要取得最後一個欄位的值是使用： **$NF**
- AWK 要取得倒數第二個欄位的值是使用： **$(NF-1)**
- 由後往前取，就繼續 -2、-3... 下去即可
- 範例：**awk '{print $(NF - 2)}'** # 從後面數來第二個

所以要抓 access.log 倒數第二欄 和 第二欄，範例如下：

- cat access.log | awk -F\\" '{print $(NF-2),$2}' # 使用 " 切割

想要過濾掉其他欄位，主要就是把不要的欄位設定為「空」，再來印出 $0 就可以了~

- $ cat access.log | awk '{$1=$2=$8=""; print $0}'

cut 也可以達成：

- $ cat access.log | cut --complement -d" " -f 1,2,8

- [使用 AWK 過濾不要的欄位 – Tsung's Blog (longwin.com.tw)](https://blog.longwin.com.tw/2019/04/linux-awk-filter-column-2019/)
- [AWK 取得欄位的「最後」與「倒數第二個值」的方法 – Tsung's Blog (longwin.com.tw)](https://blog.longwin.com.tw/2018/10/awk-get-nf-last-next-value-2018/)

# 【Shell】取得pubic ip

```shell
#curl ifconfig.me; echo
175.99.111.111
```

# 【shell】內建變數

<table border="1" cellspacing="0" id="bkmrk-%24bash_env-%C2%A0absolute-"><tbody><tr><td><span style="font-size: medium;">$BASH\_ENV</span></td><td><span style="font-size: medium;"> absolute path of startup file</span></td></tr><tr><td><span style="font-size: medium;">$CDPATH</span></td><td><span style="font-size: medium;"> directories searched by cd</span></td></tr><tr><td><span style="font-size: medium;">$FCEDIT</span></td><td><span style="font-size: medium;"> absolute path of history editor</span></td></tr><tr><td><span style="font-size: medium;">$HISTCMD</span></td><td><span style="font-size: medium;"> the history number of the current command</span></td></tr><tr><td><span style="font-size: medium;">$HISFILE</span></td><td><span style="font-size: medium;"> absolute path of history file</span></td></tr><tr><td><span style="font-size: medium;">$HISTSIZE</span></td><td><span style="font-size: medium;"> number of remembered commands</span></td></tr><tr><td><span style="font-size: medium;">$HOME</span></td><td><span style="font-size: medium;"> login directory</span></td></tr><tr><td><span style="font-size: medium;">$IFS</span></td><td><span style="font-size: medium;"> token delimiters</span></td></tr><tr><td><span style="font-size: medium;">$LINENO</span></td><td><span style="font-size: medium;"> current line number in shell script</span></td></tr><tr><td><span style="font-size: medium;">$LINES</span></td><td><span style="font-size: medium;"> terminal height</span></td></tr><tr><td><span style="font-size: medium;">$MAIL </span></td><td><span style="font-size: medium;"> absolute path of mailbox</span></td></tr><tr><td><span style="font-size: medium;">$MAILCHECK</span></td><td><span style="font-size: medium;"> number of seconds to check mail</span></td></tr><tr><td><span style="font-size: medium;">$OLDPWD</span></td><td><span style="font-size: medium;"> absolute path of previous directory</span></td></tr><tr><td><span style="font-size: medium;">$OPTARG</span></td><td><span style="font-size: medium;"> option set by getopt</span></td></tr><tr><td><span style="font-size: medium;">$OPTIND</span></td><td><span style="font-size: medium;"> option's ordinal position set by getopt</span></td></tr><tr><td><span style="font-size: medium;">$OSTYPE</span></td><td><span style="font-size: medium;"> the OS on which bash is executing</span></td></tr><tr><td><span style="font-size: medium;">$PATH</span></td><td><span style="font-size: medium;"> command search path</span></td></tr><tr><td><span style="font-size: medium;">$PPID </span></td><td><span style="font-size: medium;"> process ID of parent</span></td></tr><tr><td><span style="font-size: medium;">$PS1 </span></td><td><span style="font-size: medium;"> primary prompt</span></td></tr><tr><td><span style="font-size: medium;">$PS2</span></td><td><span style="font-size: medium;"> secondary prompt</span></td></tr><tr><td><span style="font-size: medium;">$PWD</span></td><td><span style="font-size: medium;"> absolute path of current directory</span></td></tr><tr><td><span style="font-size: medium;">$RANDOM</span></td><td><span style="font-size: medium;"> random integer</span></td></tr><tr><td><span style="font-size: medium;">$REPLY</span></td><td><span style="font-size: medium;"> default variable for read</span></td></tr><tr><td><span style="font-size: medium;">$SECONDS</span></td><td><span style="font-size: medium;"> number of seconds since shell started</span></td></tr><tr><td><span style="font-size: medium;">$SHELL</span></td><td><span style="font-size: medium;"> absolute pathname of preferred shell</span></td></tr><tr><td><span style="font-size: medium;">$TMOUT</span></td><td><span style="font-size: medium;"> seconds to log out after lack of use</span></td></tr><tr><td><span style="font-size: medium;">$UID</span></td><td><span style="font-size: medium;"> user ID of the current user</span></td></tr><tr><td><span style="font-size: medium;">$$</span></td><td><span style="font-size: medium;"> process ID of current shell</span></td></tr><tr><td><span style="font-size: medium;">$?</span></td><td><span style="font-size: medium;"> exit status of most recent statement</span></td></tr><tr><td><span style="font-size: medium;">$#</span></td><td><span style="font-size: medium;"> 參數的數目</span></td></tr><tr><td><span style="font-size: medium;">$\*</span></td><td><span style="font-size: medium;"> 代表所有參數</span></td></tr><tr><td><span style="font-size: medium;">$!</span></td><td><span style="font-size: medium;"> PID of the most recently started backgroup job</span></td></tr></tbody></table>

# 【Shell】動態呼叫awk自訂函數

\*test.sh

```shell
#!/bin/bash
awkshell="/tmp/newdf.$$"
#awkshell="/tmp/newdf"
trap "rm -f $awkshell" exit

cat << EOF > $awkshell
function showunit(size){
        mb = size / 1024;
        gb = mb / 1024;
        if ( size < 1024 || substr(size,1,1) !~ "[0-9]" || substr(size,2,1) !~ "[0-9]" ){ return size }
        else if ( mb < 1 ){ return size"K" }
        else if ( gb < 1 ){ return (int(mb*100)/100)"M" }
        else { return (int(gb*100)/100)"G"} 
}
!/Filesystem/{
        size=showunit($1)
        print size
}
EOF

#echo $awkshell
#cat $awkshell
echo $1 | awk -f $awkshell

exit 0

```

```shell
[root]$ sh test.sh 6666
6.5M
[root]$ sh test.sh 66666
65.1M
[root]$ sh test.sh 666666666
635.78G

```

# 【 Shell】【Tool】測試機器防火牆是否開通

[https://medium.com/%E8%BC%95%E9%AC%86%E5%B0%8F%E5%93%81-pks%E8%88%87k8s%E7%9A%84%E9%BB%9E%E6%BB%B4/firewall-proxy-i-hate-you-5923c77266d5](https://medium.com/%E8%BC%95%E9%AC%86%E5%B0%8F%E5%93%81-pks%E8%88%87k8s%E7%9A%84%E9%BB%9E%E6%BB%B4/firewall-proxy-i-hate-you-5923c77266d5)

```
#!/bin/bash
services=(
"gcr.io"
"checkpoint-api.hashicorp.com"
"storage.googleapis.com monitoring.googleapis.com"
"gkeconnect.googleapis.com"
"iam.googleapis.com"
"dl.google.com"
"gcr.io"
"googleusercontent.com"
"www.googleapis.com"
"accounts.google.com"
"cloudresourcemanager.googleapis.com"
"container.googleapis.com"
"gkeconnect.googleapis.com"
"gkehub.googleapis.com"
"iam.googleapis.com"
"iamcredentials.googleapis.com"
"logging.googleapis.com"
"monitoring.googleapis.com"
"oauth2.googleapis.com"
"securetoken.googleapis.com"
"servicecontrol.googleapis.com"
"serviceusage.googleapis.com"
"storage.googleapis.com"
"stackdriver.googleapis.com"
"sts.googleapis.com"
"checkpoint-api.hashicorp.com"
"releases.hashicorp.com"
"this-url-wont-work.comx"
)
for s in ${services[@]}; do
   nc -v -z -w 3 $s 443 &> /dev/null && echo "$s:443 Online" || echo "$s:443 Offline"
done
```

結果：

```
gcr.io:443 Online
checkpoint-api.hashicorp.com:443 Online
storage.googleapis.com:443 Online
monitoring.googleapis.com:443 Online
gkeconnect.googleapis.com:443 Online
iam.googleapis.com:443 Online
dl.google.com:443 Online
gcr.io:443 Online
googleusercontent.com:443 Offline
www.googleapis.com:443 Online
accounts.google.com:443 Online
cloudresourcemanager.googleapis.com:443 Online
container.googleapis.com:443 Online
gkeconnect.googleapis.com:443 Online
gkehub.googleapis.com:443 Online
iam.googleapis.com:443 Online
iamcredentials.googleapis.com:443 Online
logging.googleapis.com:443 Online
monitoring.googleapis.com:443 Online
oauth2.googleapis.com:443 Online
securetoken.googleapis.com:443 Online
servicecontrol.googleapis.com:443 Online
serviceusage.googleapis.com:443 Online
storage.googleapis.com:443 Online
stackdriver.googleapis.com:443 Online
sts.googleapis.com:443 Online
checkpoint-api.hashicorp.com:443 Online
releases.hashicorp.com:443 Online
this-url-wont-work.comx:443 Offline
```

# 【Shell】【Tool】批次更新密碼

如果有許多使用者需要批次重設密碼並強制其在下次登入時更新，可以使用一個簡單的 Shell 腳本來自動化這個過程。假設你有一個包含所有目標使用者名稱的文字檔，例如 `users.txt`，可以按照以下步驟操作：

1. **建立使用者清單**：
    
    在 `users.txt` 中列出所有需要重設密碼的使用者名稱，每行一個。例如：
    
    ```
    <span class="hljs-attribute">user1</span>
    user2
    user3
    
    ```
2. **編寫批次重設密碼的腳本**：
    
    建立一個 Shell 腳本，批次執行密碼重設並強制更新。例如，將以下內容儲存為 `reset_password.sh`：
    
    ```
    <span class="hljs-meta">#!/bin/bash
    </span>
    <span class="hljs-comment"># 檢查 users.txt 是否存在</span>
    <span class="hljs-keyword">if</span> [[ ! <span class="hljs-_">-f</span> users.txt ]]; <span class="hljs-keyword">then</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"Error: users.txt not found!"</span>
        <span class="hljs-built_in">exit</span> 1
    <span class="hljs-keyword">fi</span>
    
    <span class="hljs-comment"># 循環讀取每一個使用者</span>
    <span class="hljs-keyword">while</span> IFS= <span class="hljs-built_in">read</span> -r username; <span class="hljs-keyword">do</span>
        <span class="hljs-comment"># 跳過空行</span>
        <span class="hljs-keyword">if</span> [[ -z <span class="hljs-string">"<span class="hljs-variable">$username</span>"</span> ]]; <span class="hljs-keyword">then</span>
            <span class="hljs-built_in">continue</span>
        <span class="hljs-keyword">fi</span>
    
        <span class="hljs-comment"># 設定預設密碼或隨機密碼</span>
        new_password=<span class="hljs-string">"DefaultPassword123"</span> <span class="hljs-comment"># 或使用 `new_password=$(openssl rand -base64 12)` 生成隨機密碼</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"重設 <span class="hljs-variable">$username</span> 的密碼為 <span class="hljs-variable">$new_password</span>"</span>
    
        <span class="hljs-comment"># 重設密碼</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$username</span>:<span class="hljs-variable">$new_password</span>"</span> | sudo chpasswd
    
        <span class="hljs-comment"># 強制下次登入時更改密碼</span>
        sudo chage <span class="hljs-_">-d</span> 0 <span class="hljs-string">"<span class="hljs-variable">$username</span>"</span>
    
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$username</span> 的密碼已重設並強制更新"</span>
    <span class="hljs-keyword">done</span> < users.txt
    
    ```
3. **執行腳本**：
    
    為腳本增加執行權限，然後執行：
    
    ```
    chmod +x reset_password.<span class="hljs-keyword">sh</span>
    sudo ./reset_password.<span class="hljs-keyword">sh</span>
    
    ```
4. **檢查結果**：
    
    執行完成後，你可以再次使用 `chage -l username` 指令逐一檢查使用者的密碼到期設定，確認是否成功。

這樣便可批次重設多位使用者的密碼，並強制其在下次登入時更新密碼。

# 【Shell】【Tip】列出環境變數網址解析dns

[![image-1747466952908.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-05/scaled-1680-/nsMi0mJuXrwA3ZjZ-image-1747466952908.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-05/nsMi0mJuXrwA3ZjZ-image-1747466952908.png)

```shell
#!/bin/bash

export A_HOST="https://www.google.com/a/b"
export B_HOST="https://tw.yahoo.com/a?a=a&b=b"
export C_HOST="https://www.microsoft.com/zh-tw/#bb"

echo "列出所有環境變數並解析疑似網址的 IP："
echo "========================================="

# 顯示目前 DNS server
# 嘗試從 systemd-resolved 的配置中找出真實 DNS
if command -v resolvectl >/dev/null 2>&1; then
  dns_servers=$(resolvectl status | grep 'DNS Servers' | head -n 1 | awk -F': ' '{print $2}')
#elif [ -f /run/systemd/resolve/resolv.conf ]; then
#  dns_servers=$(grep -E '^nameserver' /run/systemd/resolve/resolv.conf | awk '{print $2}')
else
  dns_servers=$(grep -E '^nameserver' /etc/resolv.conf | awk '{print $2}')
fi

echo "目前使用的 DNS server：$dns_servers"
echo "-----------------------------------------"

# 暫存解析過的域名，避免重複查詢
declare -A resolved

while IFS='=' read -r name value; do
  if [[ "$value" =~ ^https?://([^:/]+) ]]; then
    host="${BASH_REMATCH[1]}"
  elif [[ "$value" =~ ^([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$ ]]; then
    host="${BASH_REMATCH[1]}"
  else
    continue
  fi

  if [[ -n "$host" && -z "${resolved[$host]}" ]]; then
    ip=$(dig +short "$host" | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -n 1)
    resolved["$host"]="$ip"
    echo "$name=$value"
    if [[ -z "$ip" ]]; then
      echo " ↳ $host → [無法解析]"
    else
      echo " ↳ $host → $ip"
    fi
    echo "-----------------------------------------"
  fi
done < <(env)

echo ""
echo "系統 /etc/hosts 內容（不含註解與空行）："
echo "========================================="
grep -vE '^\s*#|^\s*$' /etc/hosts
```

# 【Shell】【Tool】call_api

```shell
call_api() {
  curl -fsS \
    --connect-timeout 2 \
    --max-time 5 \
    --retry 2 --retry-delay 1 \
    -w " [HTTP_CODE=%{http_code}]\n" \
    "$URL"
}
```

好的 👍 我來逐一說明 `call_api()` 這段裡面每個 **curl** 參數的作用，並且說明為什麼要加：

```bash
call_api() {
  curl -fsS \
    --connect-timeout 2 \
    --max-time 5 \
    --retry 2 --retry-delay 1 \
    -w " [HTTP_CODE=%{http_code}]\n" \
    "$URL"
}

```

---

###  參數逐一解析

1. **`-f` / `--fail`**
    
    
    - 作用：當 HTTP 狀態碼是 **4xx / 5xx** 時，不會把 HTML 錯誤頁輸出到 stdout，而是直接返回非零 exit code。
    - 好處：可以在 script 判斷「這次呼叫失敗」，而不是誤以為拿到一大串錯誤 HTML 也算成功。

---

2. **`-s` / `--silent`**
    
    
    - 作用：靜默模式，不顯示下載進度條與錯誤訊息（錯誤還是會 exit code ≠ 0）。
    - 好處：避免 cron job 或 log 裡塞滿進度百分比。

---

3. **`-S` / `--show-error`**
    
    
    - 作用：在 `-s` 靜默模式下，仍然把錯誤訊息（例如 DNS 解析失敗、timeout）顯示出來。
    - 好處：結合 `-s` 使用，可以安靜但又不失錯誤訊息。

---

4. **`--connect-timeout 2`**
    
    
    - 作用：設定 TCP 連線的最大等待時間（單位：秒）。
    - 例子：如果 API 服務根本沒在聽 port 8082，curl 會在 **2 秒** 內放棄。

---

5. **`--max-time 5`**
    
    
    - 作用：整個 request（連線、傳輸、讀取）的最長時間，超過就中斷。
    - 好處：避免 API 掛住時，curl 一直卡在那裡。

---

6. **`--retry 2`**
    
    
    - 作用：遇到「暫時性錯誤」（像 DNS 解析失敗、timeout、connection reset by peer），會自動重試最多 2 次。
    - 預設只會 retry network-level 的錯誤，不會 retry 4xx/5xx。

---

7. **`--retry-delay 1`**
    
    
    - 作用：每次 retry 前延遲 **1 秒**。
    - 預設會 delay 1 秒，但這裡寫死確保一致。

---

8. **`-w " [HTTP_CODE=%{http_code}]\n"`**
    
    
    - 作用：在 response 輸出結尾，附加 HTTP 狀態碼。
    - 例子：成功時會多一行 ` [HTTP_CODE=200]`
    - 好處：log 裡可以直接看出 API 狀態。

---

9. **`"$URL"`**
    
    
    - 作用：實際要請求的 URL。
    - 這裡設成變數，方便修改。

---

###  總結

這樣組合的意圖就是：

- `-fsS` → 安靜，但保留錯誤訊息，不要 HTML 錯誤頁
- `--connect-timeout` / `--max-time` → 控制時間上限，避免卡死
- `--retry` / `--retry-delay` → 短暫網路錯誤自動補救
- `-w %{http_code}` → log 裡清楚顯示 HTTP 狀態碼

---

# 【茶包射手】

# version `GLIBCXX_3.4.20' not found 解决方法

[https://www.jianshu.com/p/050b2b777b9d](https://www.jianshu.com/p/050b2b777b9d)

# 【cron】沒有依排程執行

### 檢查服務

```shell
# systemctl status crond
● crond.service - Command Scheduler
   Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2024-04-22 13:11:51 CST; 7min ago
 Main PID: 17677 (crond)
   CGroup: /system.slice/crond.service
           └─17677 /usr/sbin/crond -n

Apr 22 13:11:51 localhost.localdomain systemd[1]: Started Command Scheduler.
Apr 22 13:11:51 localhost.localdomain crond[17677]: (CRON) INFO (RANDOM_DELAY will be scaled with factor 74% if used.)
Apr 22 13:11:51 localhost.localdomain crond[17677]: (CRON) INFO (running with inotify support)
Apr 22 13:11:51 localhost.localdomain crond[17677]: (CRON) INFO (@reboot jobs will be run at computer's startup.)


# 如果是ubuntu服務是 cron

# 如果沒有起請將服務起來
#  systemctl start crond
# 設定開機自動起服務
#  systemctl enable crond
```

### 檢查log

```shell
# 檢查cron log
cat /var/log/cron

# 檢查sys log
grep CRON /var/log/messages
```

# 【網路相關】

# 【Linux】【Ubuntu】如何修改 ip

以下提供在 **Ubuntu 18.04 以後（netplan）** 與 **Ubuntu 16.04 舊版（/etc/network/interfaces）** 的常見修改方式，請依照你實際版本選擇。

---

## 1️⃣ 確認目前網路設定工具

Ubuntu 18.04 以後預設使用 **Netplan**

```bash
ls /etc/netplan/

```

若此資料夾存在 YAML 檔案（例如 `00-installer-config.yaml`），代表使用 Netplan。

---

## 2️⃣ Netplan 修改（Ubuntu 18.04+）

1. **查詢目前網卡名稱**
    
    ```bash
    ip link
    
    ```
    
    例如 `ens33`、`enp0s3`。
2. **編輯設定檔**
    
    ```bash
    sudo nano /etc/netplan/00-installer-config.yaml
    
    ```
    
    內容範例：
    
    ```yaml
    network:
      version: 2
      renderer: networkd
      ethernets:
        ens33:
          dhcp4: no
          addresses:
            - 192.168.1.100/24
          gateway4: 192.168.1.1
          nameservers:
            addresses: [8.8.8.8, 8.8.4.4]
    
    ```
3. **套用設定**
    
    ```bash
    sudo netplan apply
    
    ```
    
    建議第一次可用 `sudo netplan try`，若有誤會自動還原。

---

## 3️⃣ 傳統 /etc/network/interfaces（Ubuntu 16.04 或自行安裝 ifupdown）

1. 編輯：
    
    ```bash
    sudo nano /etc/network/interfaces
    
    ```
2. 範例：
    
    ```
    auto ens33
    iface ens33 inet static
        address 192.168.1.100
        netmask 255.255.255.0
        gateway 192.168.1.1
        dns-nameservers 8.8.8.8 8.8.4.4
    
    ```
3. 套用：
    
    ```bash
    sudo systemctl restart networking
    
    ```

---

## 4️⃣ 臨時修改 IP（重開機後失效）

若只想暫時修改：

```bash
sudo ip addr add 192.168.1.100/24 dev ens33
sudo ip route add default via 192.168.1.1

```

---

## 5️⃣ 驗證設定

```bash
ip addr show
ip route
ping 8.8.8.8

```

---

### 小提醒

- 若同時使用 **VPN** 或 **Cloud Provider（AWS/GCP/Azure）**，請確認平台的網卡策略與安全群組。
- 修改前最好備份設定檔：
    
    ```bash
    sudo cp /etc/netplan/00-installer-config.yaml /etc/netplan/00-installer-config.yaml.bak
    
    ```

以上即可依版本與需求選擇永久或臨時的 Ubuntu IP 修改方式。

---

###  如果出現警告畫面

[![image-1757918219117.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-09/scaled-1680-/xbg0Z279VoutyXEI-image-1757918219117.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-09/xbg0Z279VoutyXEI-image-1757918219117.png)

從這張截圖可以看到兩類主要警告訊息：

---

## 1️⃣ 檔案權限過開 (Permissions are too open)

```
Permissions for /etc/netplan/00-installer-config.yaml are too open.
Netplan configuration should NOT be accessible by others.

```

**原因**：  
Netplan 要求設定檔必須只有 root 可以寫，否則 `netplan apply` 會拒絕。

**解法**：  
把檔案權限調整為 600 或 640，例如：

```bash
sudo chmod 600 /etc/netplan/00-installer-config.yaml
# 或若需要同群組讀取
sudo chmod 640 /etc/netplan/00-installer-config.yaml

```

再確認：

```bash
ls -l /etc/netplan/00-installer-config.yaml
# -rw------- 或 -rw-r----- 都可以

```

---

## 2️⃣ `gateway4` 已被棄用

```
'gateway4' has been deprecated, use default routes instead.

```

Ubuntu 22.04 之後 `gateway4` 舊語法建議改成 `routes:` 或 `default`。

**修改範例**  
舊：

```yaml
ethernets:
  ens33:
    dhcp4: no
    addresses: [192.168.1.100/24]
    gateway4: 192.168.1.1

```

新：

```yaml
ethernets:
  ens33:
    dhcp4: no
    addresses: [192.168.1.100/24]
    routes:
      - to: default
        via: 192.168.1.1

```

---

## 3️⃣ 其他訊息

```
WARNING:root:Cannot call Open vSwitch: ovsdb-server.service is not running.

```

如果沒有使用 Open vSwitch 可以忽略；若有需要，就啟動服務：

```bash
sudo systemctl enable --now ovsdb-server

```

---

### 建議修正步驟總結

1. **調整權限**
    
    ```bash
    sudo chmod 600 /etc/netplan/00-installer-config.yaml
    
    ```
2. **修改 YAML 把 `gateway4` 換成 routes**
3. 驗證語法：
    
    ```bash
    sudo netplan try
    
    ```
    
    沒問題後：
    
    ```bash
    sudo netplan apply
    
    ```

這樣就能消除「Permissions too open」與「gateway4 deprecated」兩個警告。

# 【Top】 效能監控工具

[![image-1684291907122.png](https://bookstack.treemanou.com/uploads/images/gallery/2023-05/scaled-1680-/9QGXIeNTMSlf7Gvh-image-1684291907122.png)](https://bookstack.treemanou.com/uploads/images/gallery/2023-05/9QGXIeNTMSlf7Gvh-image-1684291907122.png)

top - 16:12:13 up 42 days, 37 min, 6 users, **<span style="color:#ff0000;">load average</span>**: 1.66, 1.67, 2.39  
Tasks: 255 total, 8 running, 247 sleeping, 0 stopped, 0 zombie  
Cpu0 : 31.4%us, 51.5%sy, 0.0%ni, 10.0%id, 0.0%wa, 0.0%hi, 6.7%si, 0.3%st  
Cpu1 : 89.0%us, 9.0%sy, 0.0%ni, 2.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st  
Cpu2 : 31.4%us, 51.8%sy, 0.0%ni, 15.4%id, 0.0%wa, 0.0%hi, 1.3%si, 0.0%st  
Cpu3 : 34.2%us, 52.5%sy, 0.0%ni, 7.6%id, 0.0%wa, 0.0%hi, 5.6%si, 0.0%st  
Mem: 16269604k total, 15986032k used, 283572k free, 1069332k buffers  
Swap: 4193272k total, 0k used, 4193272k free, 3629176k cached

Linux上的top命令是一個非常有用的工具，它可以顯示系統中正在運行的進程的詳細信息，包括CPU、記憶體、磁盤和網路使用情況。以下是一些使用top命令的基本說明：

1\. 開啟top：在終端窗口中輸入`top`並按下Enter鍵，即可開啟top命令的監視模式。

2\. 介面說明：top的介面由多個欄位組成，每個欄位提供了特定的系統信息。其中一些常見的欄位包括：

\- PID：進程ID。  
\- USER：執行進程的使用者名稱。  
\- PR：進程的優先級。  
\- NI：進程的優先級數值。  
\- VIRT：進程使用的虛擬記憶體大小。  
\- RES：進程使用的實體記憶體大小。  
\- SHR：進程共享的記憶體大小。  
\- %CPU：進程使用的CPU百分比。  
\- %MEM：進程使用的記憶體百分比。  
\- TIME+：進程運行的總時間。  
\- COMMAND：進程的命令名稱。

3\. 排序方式：預設情況下，top按照CPU使用百分比（%CPU）進行排序。你可以按下鍵盤上的`&lt;`或`&gt;`鍵來改變排序方式。例如，按下`&lt;`鍵可以按照進程ID（PID）進行排序，按下`&gt;`鍵可以按照記憶體使用百分比（%MEM）進行排序。

4\. 刷新頻率：預設情況下，top每隔3秒刷新一次顯示的進程列表。你可以按下鍵盤上的`s`鍵來改變刷新頻率，然後輸入所需的秒數。

5\. 執行命令：在top界面中，你可以執行一些特定的命令，例如：

\- `k`：終止選中的進程。輸入進程的PID並按下Enter鍵，然後選擇終止原因。  
\- `r`：修改進程的優先級。輸入進程的PID並按下Enter鍵，然後輸入新的優先級數值。  
\- `1`：切換到全局(CPU)總覽模式。  
\- `h`：顯示top命令的幫助信息。

6\. q :退出top

---

### Help 說明

Z、B、E、e：全局設置：'Z' 切換彩色顯示；'B' 切換粗體顯示；'E' 或 'e' 調整總結/任務記憶體的刻度。

l、t、m：切換總結顯示：'l' 切換載入平均值；'t' 切換任務/ CPU 狀態；'m' 顯示記憶體信息。

0、1、2、3、I：切換顯示：'0' 切換顯示 0 值；'1/2/3' 切換 CPU 或 NUMA 节點視圖；'I' 切換到 Irix 模式。

f、F、X：欄位設置：'f'/'F' 添加/移除/調整/排序欄位；'X' 增加固定寬度。

L、&amp;、&lt;、&gt;：定位操作：'L'/'&amp;' 查找/重複查找；移動排序列：'&lt;'/'&gt;' 左移/右移。

R、H、V、J：切換設置：'R' 切換排序方式；'H' 切換線程顯示；'V' 切換樹狀視圖；'J' 切換數字對齊。

c、i、S、j：切換設置：'c' 切換命令名稱/命令行顯示；'i' 切換空閒時間顯示；'S' 切換時間顯示；'j' 切換字符串對齊。

x、y：切換突出顯示：'x' 切換排序欄位的突出顯示；'y' 切換運行的任務突出顯示。

z、b：切換設置：'z' 切換彩色/單色顯示；'b' 切換粗體/反轉顯示（僅在 'x' 或 'y' 選項下）。

u、U、o、O：過濾條件：'u'/'U' 過濾按有效/任意使用者；'o'/'O' 過濾其他條件。

n、#、^O：設定顯示的最大任務數量：'n'/'#' 設定最大顯示任務數；按下 Ctrl+'O' 顯示其他過濾條件。

C、...：切換滾動座標顯示：'up'、'down'、'left'、'right'、'home'、'end'

k、r：操作任務：'k' 終止任務；'r' 修改任務的優先級。

d 或 s：設定更新間隔。

W、Y：寫入配置文件 'W'；檢查其他輸出 'Y'。

---

**load average 負載平均**

load average: 1.66, 1.67, 2.39  
最近 1分鐘 / 5分鐘 / 15分鐘

單核cpu來看，load值大於1代表系統滿載，load值為0代表完全空閒，理想數字在**0.7**以下會比較好  
如果是4核心則為0.7\*4 = 2.8以下比較好

參考  
[https://www.itread01.com/content/1548662434.html](https://www.itread01.com/content/1548662434.html)

---

us: is meaning of "user CPU time"  
sy: is meaning of "system CPU time"  
ni: is meaning of" nice CPU time"  
id: is meaning of "idle"  
wa: is meaning of "iowait"   
hi：is meaning of "hardware irq"  
si : is meaning of "software irq"  
st : is meaning of "steal time

---

#### Top 排序

在Linux中，`top`命令可以用来查看系统的实时性能信息。默认情况下，`top`的显示界面按照CPU使用率降序排列进程。但是，你可以通过按键来改变排序方式。以下是一些常用的排序键：

1\. \*\*按CPU使用率排序：\*\*  
\- 按下 `<span style="background-color:#ffff99;">**Shift + P**</span>`

2\. \*\*按内存使用率排序：\*\*  
\- 按下 `<span style="background-color:#ffff99;">Shift + M</span>`

3\. \*\*按累计CPU使用率排序：\*\*  
\- 按下 `t` 键，然后选择 `%CPU`

4\. \*\*按累计内存使用率排序：\*\*  
\- 按下 `t` 键，然后选择 `%MEM`

5\. \*\*按PID（进程ID）排序：\*\*  
\- 按下 `Shift + N`

6\. \*\*按进程名称排序：\*\*  
\- 按下 `c` 键

7\. \*\*按照其他列排序：\*\*  
\- 在`top`的显示界面中，你可以使用左右箭头键来选择不同的列，然后按下 `o` 键来选择要排序的列。

8\. \*\*切换升序/降序排序：\*\*  
\- 在排序之后，你可以按下 `R` 键来切换排序的顺序（升序或降序）。

请注意，这些键仅在`top`运行时有效，按下相应的键后，`top`将根据你的选择重新排序并更新显示。如果你希望在`top`启动时就按照特定的排序方式显示，你可以使用`top`命令的一些参数，例如：

```shell
top -o %CPU # 按照 CPU 使用率降序排列
top -o %MEM # 按照内存使用率降序排列
```

这些是一些基本的排序操作，根据你的需求可以选择适合的排序方式。

---

# 🖥 CPU 使用率指標速查表

<table id="bkmrk-%E6%8C%87%E6%A8%99-%E5%85%A8%E5%90%8D-%E6%84%8F%E7%BE%A9-%E5%88%A4%E6%96%B7%E6%A8%99%E6%BA%96-us-use"><thead><tr><th>指標</th><th>全名</th><th>意義</th><th>判斷標準</th></tr></thead><tbody><tr><td>**us**</td><td>user</td><td>使用者態 CPU（應用程式運算時間）</td><td>- **高代表程式在跑計算**（正常）- 長期 80–90% 可能 CPU 算力不足</td></tr><tr><td>**sy**</td><td>system</td><td>核心態 CPU（系統呼叫/驅動/中斷）</td><td>- 偶爾 5–15% 正常- 長期 &gt;30% 可能系統呼叫太頻繁或 driver 問題</td></tr><tr><td>**id**</td><td>idle</td><td>CPU 閒置時間</td><td>- 高表示 CPU 很空閒- 低（接近 0）表示 CPU 幾乎滿載</td></tr><tr><td>**wa**</td><td>iowait</td><td>CPU 在等 I/O（磁碟/網路）</td><td>- **0–5% 正常**- **&gt;20% 有 I/O 壓力**- **&gt;50% 嚴重瓶頸**，CPU 幾乎都在等磁碟/網路- **100%** = 完全被 I/O 卡住</td></tr></tbody></table>

---

## 📊 看 `top` 的例子

```
%Cpu0 : 12.0 us,  7.0 sy,  0.0 ni,  0.0 id, 81.0 wa,  0.0 hi,  0.0 si,  0.0 st

```

解讀：

- user 12% → 應用程式在跑
- system 7% → kernel 在處理系統呼叫
- idle 0% → CPU 沒閒下來
- wa 81% → CPU 大部分時間都在等磁碟/網路 → **I/O 瓶頸**

---

## 📌 判斷邏輯

1. **us 高** → 程式運算密集（算力不足）
2. **sy 高** → 核心忙，可能 driver、系統呼叫過多
3. **wa 高** → I/O 磁碟/網路瓶頸
4. **id 低** → CPU 幾乎被榨乾

---

## ✅ 快速結論

- **CPU 不夠用** → us 高、id 低、wa 低
- **系統呼叫過多** → sy 高
- **磁碟/網路卡住** → wa 高
- **CPU 很空閒** → id 高

---

要不要我幫你再做一張 **「CPU 狀態 → 問題來源 → 解決方法」的對照表**，讓你排查更直觀？

# 【資安】強制登出使用者

##### 使用who查詢在線使用者

##### pkill -kill -t {tty} 強制登出

[![image-1633573964383.png](http://bookstack.treemanou.com/uploads/images/gallery/2021-10/scaled-1680-/8uPHXLVQMO0TK3cG-image-1633573964383.png)](http://bookstack.treemanou.com/uploads/images/gallery/2021-10/8uPHXLVQMO0TK3cG-image-1633573964383.png)

# 【圖形介面】Linux安裝圖形介面

\* 安裝圖形介面  
```  
yum groupinstall -y "GNOME Desktop"  
```  
|run level|&lt;|h  
|0|關機(``不可設為預設``)|  
|6|重新開機(``不可設為預設``)|  
|1|單人文字模式|  
|2|多人模式文字(無網路)|  
|3|多人模式文字(有網路)|  
|4|客製化模式3|  
|5|多人模式圖形(有網路)|  
```  
系統使用"targets"取代以前的"run-levels"。下列為主要的兩個設定值：  
multi-user.target: 表示 runlevel 3  
graphical.target: 表示 runlevel 5  
  
檢查現在系統的預設target的指令：systemctl get-default  
```  
\*查看現在啟動  
```  
\[snowFlake@www ~\]$ sudo systemctl get-default  
multi-user.target  
```  
\*設定圖形啟動  
```  
\[snowFlake@www ~\]$ sudo systemctl set-default  
graphical.target  
```  
\*忘記指令可以查看/etc/inittab 檔案  
```  
\# cat /etc/inittab  
\# inittab is no longer used when using systemd.  
\#  
\# ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.  
\#  
\# Ctrl-Alt-Delete is handled by /usr/lib/systemd/system/ctrl-alt-del.target  
\#  
\# systemd uses 'targets' instead of runlevels. By default, there are two main targets:  
\#  
\# multi-user.target: analogous to runlevel 3  
\# graphical.target: analogous to runlevel 5  
\#  
\# To view current default target, run:  
\# systemctl get-default  
\#  
\# To set a default target, run:  
\# systemctl set-default TARGET.target  
\#  
```

# 【圖形介面】tiger vnc

#### 安裝

```
yum install tigervnc-server -y
```

VNC 預設 port 為 5900，而 :1 代表一個虛擬桌面，所以連結這個虛擬桌面的 port 為 5901（5900 + 1）或 1

複製設定檔

```shell
cp /lib/systemd/system/vncserver@.service /etc/systemd/system/vncserver@:1.service
```

以下是用root 帳號，如果是一般使用者，可使用註解區塊(使用者jcky，把root註解)

```shell
[Unit]
Description=Remote desktop service (VNC)
After=syslog.target network.target

[Service]
Type=simple

# Clean any existing files in /tmp/.X11-unix environment
ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill %i > /dev/null 2>&1 || :'
ExecStop=/bin/sh -c '/usr/bin/vncserver -kill %i > /dev/null 2>&1 || :'

# 一般帳號
# #ExecStart=/usr/sbin/runuser -l jacky -c "/usr/bin/vncserver %i"
# #PIDFile=/home/jacky/.vnc/%H%i.pid
#
# root 帳號
ExecStart=/usr/sbin/runuser -l root -c "/usr/bin/vncserver %i -geometry 1280x1024"
PIDFile=/root/.vnc/%H%i.pid

[Install]
WantedBy=multi-user.target

```

設定VNC密碼

如果是一般使用者要先切換到該身分下

```
su jacky
```

設定vnc 密碼

```shell
vncpasswd
```

使用root 帳號

```
su -
systemctl daemon-reload
```

啟用服務，並設定開機自動執行

```
systemctl start vncserver@:1.service
systemctl enable vncserver@:1.service
```

可使用vnc

# 【Linux】帳號管理

新增/刪除帳號

```shell
# -u 自訂UID
# -g 加入某群組，可使用GID, 或是群組名
# -d 自定使用的家目錄
# -M 不建立家目錄
# -s 自訂shell
# 新增帳號(包含家目錄)
# centos
usradd 新帳號名稱
# ubuntu
adduser 新帳號名稱



# 刪除帳號(保留家目錄) 
userdel user1
# 刪除帳號(刪除家目錄) 
userdel -r user1


# 修改帳號
usermod [-LU][-c <備註>][-d <登入目錄>][-e <有效期限>] [-f <緩沖天數>][-g <群組>][-G <群組>][-l <帳號名稱>][-s ] [-u ][用戶帳號]
#-c<備註> 修改用戶帳號的備註文字。
#-d登入目錄> 修改用戶登入時的目錄。
#-e<有效期限> 修改帳號的有效期限。
#-f<緩沖天數> 修改在密碼過期後多少天即關閉該帳號。
#-g<群組> 修改用戶所屬的群組。
#-G<群組> 修改用戶所屬的附加群組。
#-l<帳號名稱> 修改用戶帳號名稱。
#-L 鎖定用戶密碼，使密碼無效。
#-s 修改用戶登入後所使用的shell。
#-u 修改用戶ID。
#-U 解除密碼鎖定。

# user1 加入 root 群組
usermod -aG root user1
# user1 移出 root 群組
gpasswd -d root user1

```

設定密碼過期

```shell
#chage：密碼失效是通過此命令來管理的。
#參數意思：
#-m 密碼可更改的最小天數。為零時代表任何時候都可以更改密碼。
#-M 密碼保持有效的最大天數。
#-W 用戶密碼到期前，提前收到警告信息的天數。
#-E 帳號到期的日期。過了這天，此帳號將不可用。
#-d 上一次更改的日期
#-i 停滯時期。如果一個密碼已過期這些天，那麽此帳號將不可用。
#-l 例出當前的設置。由非特權用戶來確定他們的密碼或帳號何時過期。

# 設定密碼過期時間0天(使用者登入需更改密碼)
sudo chage -d 0 使用者名稱
# 設定密碼永不過期
sudo chage -M -1 使用者名稱
# 設定帳號不會過期
sudo usermod -e '' 使用者名稱
# 設定帳號密碼都不會過期
sudo chage -M -1 -E -1 使用者名稱
```

禁止 root ssh 登入

```shell
# 修改/etc/ssh/sshd_config  
vim /etc/ssh/sshd_config  

# PermitRootLogin yes 
# 改為
PermitRootLogin no

# 重新啟動 ssh服務
systemctl restart sshd

```

群駔相關

```shell
# 新增群組
# groupadd [-g GID] 群組名
groupadd group1 

# 刪除群組
# groupdel群組名
groupdel group1

# 查詢使用者群組
# 方法一 groups 使用者
groups webmaster
########################/*
[root@test webmon]# groups  webmaster
webmaster : webmaster nginx
#########################*/
# 方法二 cat /etc/group
cat /etc/group | grep webmaster
########################/*
[root@webmon205 webmon]# cat /etc/group | grep webmaster
webmaster:x:1000:webmaster
nginx:x:979:webmaster
#########################*/

# 將使用者加入某群組
# ex.將user1加入ftp群組
usermod -a -G ftp user1
# ex.將user1移出ftp群組
gpasswd -d ftp app

# 修改主群組
# ex.將user1主群組改為nginx
usermod -g nginx user1

```

# 【Linux】記憶體相關

[https://huenlil.pixnet.net/blog/post/26822270](https://huenlil.pixnet.net/blog/post/26822270)

釋放記憶體

```shell
echo 1 > /proc/sys/vm/drop_cache
```

釋放dentries、inodes所用的 cache memory

```
echo 2 > /proc/sys/vm/drop_caches
```

釋放pagecache、dentry、inode 所用的 cache memory

```shell
 echo 3 > /proc/sys/vm/drop_caches
```

完全釋放cache memory，<span style="color:#FF0000;">必須先執行sync</span>，避免錯誤。

```shell
sync

#在釋放記憶體後再將/proc/sys/vm/drop_caches的值設為0
echo 0 > /proc/sys/vm/drop_caches
```

釋放swap

```shell
 #此swap 在/dev/cobd1
 swapoff /dev/cobd1;swapon /dev/cobd1
```

常用記憶體指令

```shell
 free
 
 vmstat
 
 top
  
 watch cat /proc/meminfo
```

# 【crontab】排程相關

使用nginx 設定排程

```shell
sudo su -c "crontab -e" nginx -s /bin/bash
```

# 【vim】快速鍵

1\. vi 編輯器中跳到檔案的第一行：

 a 輸入 :0 或者 :1 回車

 b 鍵盤按下 小寫 gg

2.vi 編輯器跳到檔案最後一行：

 a 輸入 :$ 回車

 b 鍵盤按下大寫 G

 c 鍵盤按 shift + g (其實和第二種方法一樣)

**Vim快速移動游標至行首和行尾**

 1、 需要按行快速移動游標時，可以使用鍵盤上的編輯鍵Home，快速將游標移動至當前行的行首。除此之外，也可以在命令模式中使用快捷鍵"^"（即Shift+6）或0（數字0）。

 2、 如果要快速移動游標至當前行的行尾，可以使用編輯鍵End。也可以在命令模式中使用快捷鍵"$"（Shift+4）。與快捷鍵"^"和0不同，快捷鍵"$"前可以加上數字表示移動的行數。例如使用"1$"表示當前行的行尾，"2$"表示當前行的下一行的行尾。

**Vim多行註解**

多行注释：

1. 进入命令行模式，按ctrl + v进入 visual block模式（可视快模式），然后按j, 或者k选中多行，把需要注释的行标记起来
2. 按大写字母I，再插入注释符，例如//
3. 按esc键就会全部注释了（我的是按两下）

取消多行注释：

1. 进入命令行模式，按ctrl + v进入 visual block模式（可视快模式），按小写字母l横向选中列的个数，例如 // 需要选中2列
2. 按字母j，或者k选中注释符号
3. 按d键就可全部取消注释

# 【Linux】時區設定

Centos7

```shell
timedatectl set-timezone Asia/Taipei
```

# 【Linux】載入順序

[![image-1677741051902.png](https://bookstack.treemanou.com/uploads/images/gallery/2023-03/scaled-1680-/LLodC7RIbDzWWSyf-image-1677741051902.png)](https://bookstack.treemanou.com/uploads/images/gallery/2023-03/LLodC7RIbDzWWSyf-image-1677741051902.png)

[https://blog.miniasp.com/post/2021/07/26/Bash-and-Zsh-Initialization-Files](https://blog.miniasp.com/post/2021/07/26/Bash-and-Zsh-Initialization-Files)

- 這張圖的第一層 `Interactive?` 的意思
    
    Yes: 當你透過 Console 或 SSH 登入 Linux 主機，預設會進入**互動模式** (Interactive shell)，也就是這裡說的 **Interactive** 的意思。
    
    No: 任何透過 `bash -c '<command>'` 去執行的腳本，就屬於 **非互動模式** (Non-Interactive shell) 的執行。
- 這張圖第二層的 `Login shell?` 的意思
    
    基本上，透過 Console 或 SSH 登入 Linux 主機時，這個 Shell 就跑在所謂的 **Login shell** 模式下！
    
    不過，當你透過 SSH 遠端執行一個命令，此時就不會啟動 **Login shell** 模式。而直接呼叫一個使用 Bash 執行的腳本，也不是 **Login shell** 模式。例如以下命令：
    
    ```sh
    ssh user@host <COMMAND>
    
    ```
- 這張圖第二層的 `--login ?` 的意思
    
    就算你的 Bash 不是執行在 Login shell 模式，你一樣可以在呼叫 `/bin/bash` 的時候特別加上 `--login` 參數，這樣也可以被視為是 **Login shell** 模式。
    
    例如以下這段範例，就會被視為使用 Login shell 模式啟動：
    
    ```sh
    <span class="hljs-meta">#!/bin/bash --login</span>
    
    ```
- 這張圖第三層的 `$BASH_ENV` 是什麼環境變數？
    
    當你將 Bash 啟動在**非互動模式**，也沒有特別加上 `--login` 參數的情況，同時這也是大多數執行的預設值，Bash 會優先尋找目前的環境變數中有沒有一個名為 `$BASH_ENV` 的變數，這個變數其實是指向一個檔案路徑。你從 `man bash` 可以發現，Bash 在**非互動模式**啟動的時候，預設會執行以下命令：
    
    ```sh
    <span class="hljs-keyword">if</span> [ -n <span class="hljs-string">"<span class="hljs-variable">$BASH_ENV</span>"</span> ]; <span class="hljs-keyword">then</span> . <span class="hljs-string">"<span class="hljs-variable">$BASH_ENV</span>"</span>; <span class="hljs-keyword">fi</span>
    
    ```
    
    這也意味者，他會去 sourcing `"$BASH_ENV"` 這個檔案！
- 這張圖第三層的 `--noprofile` 是什麼參數？
    
    執行 `/bin/bash` 的時候，可以額外加上 `--noprofile` 參數，加上之後就不會載入任何啟動檔。
    
    如果你沒有加上 `--noprofile` 參數，也是一般大多數命令的執行方式，預設是會先載入系統全域的 `/etc/profile` 檔案。
    
    > 注意：系統全域的 `/etc/profile` 檔案，還會額外載入 `/etc/profile.d/*.sh` 檔案。
    
    然後再從 `$HOME` 目錄下**依序**找到這三個檔案執行，但重點是，**他只會選擇一個檔案來執行**，先找到的先執行，後面的就不會執行！
    
    
    1. `~/.bash_profile`
    2. `~/.bash_login`
    3. `~/.profile`
    
    由於上述三個檔案，最終只有一個會被執行，所以這絕對是一個潛在的**地雷**！💥
    
    我在多年前，就曾經因為我的 `$HOME` 目錄下同時出現 `~/.bash_profile` 與 `~/.profile` 而發生系統異常，原來只要目錄中有出現 `~/.bash_profile` 檔案，就再也不會載入 `~/.profile` 檔案啊！！！
    
    > 在 Ubuntu 的作業系統中，預設是看不到 `~/.bash_profile` 檔案的，建議都以 `~/.profile` 為主要的登入啟動檔！
    
    然而，大多數的 `~/.profile` 登入啟動檔，都會在檔案中額外載入 `~/.bashrc` 檔案，因此有些 Bash 相關的環境設定，如 `shopt` 之類的，都會放在 `~/.bashrc` 檔案中。
- 這張圖第三層的 `--rcfile <file>` 是什麼參數？
    
    當你不是以 Login shell 的方式啟動 Bash，啟動時也沒加上 `--login` 參數，他就會去找你有沒有特別加上 `--rcfile <file>` 參數，明確載入你所指定的檔案路徑。
    
    如果你額外加上的是 `--norc` 參數的話，那就代表你完全不想載入系統全域的 `/etc/bash.bashrc` 與 `$HOME` 目錄下的 `~/.bashrc` 檔案。
    
    如果你用 `--rcfile <file>` 參數找不到指定的檔案，或完全沒用 `--rcfile <file>` 或 `--norc` 參數，也是大多數命令預設的方式，那麼 Bash 就會依序載入 `/etc/bash.bashrc` 與 `~/.bashrc` 檔案，兩個檔案都會載入。這種情境下，是不會載入 `~/.bash_profile`, `~/.bash_login` 或 `~/.profile` 檔案的！

可以完整釐清 Bash 所有啟動檔的載入條件與順序，心裡的感覺格外踏實，非常棒！ 👍

### 完整的 Zsh 啟動檔載入順序

由於許多 macOS 用戶都採用 Zsh 為主，這邊我也特別研究了一下 Zsh 的啟動檔載入順序，基本上 Zsh 會依據以下順序載入：

1. `~/.zshenv`
    
    任何啟動情境下，都會載入這個檔案，請將各種環境變數請全部設定在這裡。
2. `/etc/zsh/zprofile` 與 `~/.zprofile`
    
    如果執行在 Login shell 才會依序執行 `/etc/zsh/zprofile` 與 `~/.zprofile` 檔案。
3. `/etc/zsh/zshrc` 與 `~/.zshrc`
    
    如果執行在 Interactive 互動模式下，才會依序執行 `/etc/zsh/zshrc` 與 `~/.zshrc` 檔案。
4. `/etc/zsh/zlogin` 與 `~/.zlogin`
    
    如果執行在 Login shell 下，最後才會依序執行 `/etc/zsh/zlogin` 與 `~/.zlogin` 檔案。
5. `~/.zlogout` 與 `/etc/zsh/zlogout`
    
    當你用 `exit` 或 `logout` 命令登出時，會自動依序執行 `~/.zlogout` 與 `/etc/zsh/zlogout` 檔案。

我只能說，Zsh 的啟動順序實在比 Bash 好理解太多了，也沒什麼地雷！ 👍

### 總結幾種常見情境

1. 直接呼叫某個 Shell Script
    
    ```sh
    <span class="hljs-meta">#!/bin/bash</span>
    [ -z <span class="hljs-string">"<span class="hljs-variable">$PS1</span>"</span> ] && <span class="hljs-built_in">echo</span> <span class="hljs-string">"Non-Interactive"</span> || <span class="hljs-built_in">echo</span> <span class="hljs-string">"Interactive"</span>
    <span class="hljs-built_in">shopt</span> -q login_shell && <span class="hljs-built_in">echo</span> <span class="hljs-string">"Login shell"</span> || <span class="hljs-built_in">echo</span> <span class="hljs-string">"Not login shell"</span>
    
    ```
    
    
    - `Interactive?` 👉 `No`
    - `Login shell?` 👉 `No`
    - `--noprofile?` 👉 `No`
    
    執行時完全不會載入任何新的啟動檔設定！
2. 使用 SSH 登入遠端主機
    
    ```sh
    ssh user@host
    
    ```
    
    依序載入 `/etc/profile` --&gt; `/etc/bash.bashrc` --&gt; `~/.profile` \[ -&gt; `~/.bashrc` \]
    
    
    - `Interactive?` 👉 `Yes`
    - `Login shell?` 👉 `Yes`
    - `--noprofile?` 👉 `No`
    
    > 注意: Bash 不會主動載入 `~/.bashrc` 執行，而是我們通常在 `~/.profile` 會載入 `~/.bashrc` 執行。
3. 使用 SSH 登入遠端主機後，執行 `bash` 命令
    
    ```sh
    ssh user@host
    
    ```
    
    剛登入，依序載入 `/etc/profile` --&gt; `/etc/bash.bashrc` --&gt; `~/.profile` \[ -&gt; `~/.bashrc` \]
    
    
    - `Interactive?` 👉 `Yes`
    - `Login shell?` 👉 `Yes`
    - `--noprofile?` 👉 `No`
    
    然後我們在遠端主機執行 `bash` 命令，進入下一層 Shell 環境：
    
    ```sh
    bash
    
    ```
    
    登入後執行 `bash` 預設是**互動模式**，會依序載入 `/etc/bash.bashrc` --&gt; `~/.bashrc`
    
    
    - `Interactive?` 👉 `Yes`
    - `Login shell?` 👉 `No`
    - `--rcfile <file> ?` 👉 `No`
    - `--norc ?` 👉 `No`
4. 使用 SSH 登入遠端主機後，執行 `bash -c '<command>'` 命令
    
    ```sh
    ssh user@host
    
    ```
    
    剛登入，依序載入 `/etc/profile` --&gt; `/etc/bash.bashrc` --&gt; `~/.profile` \[ -&gt; `~/.bashrc` \]
    
    
    - `Interactive?` 👉 `Yes`
    - `Login shell?` 👉 `Yes`
    - `--noprofile?` 👉 `No`
    
    ```sh
    bash -c <span class="hljs-string">'[ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"'</span>
    
    ```
    
    登入後執行 `bash -c` 命令屬於非互動模式，而且沒有 `$BASH_ENV` 的情況下，不會載入任何啟動檔！
    
    
    - `Interactive?` 👉 `No`
    - `Login shell?` 👉 `No`
    - `$BASH_ENV` 👉 `No`
5. 使用 SSH 執行遠端命令
    
    ```sh
    ssh user@host <span class="hljs-string">'[ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"'</span>
    ssh user@host <span class="hljs-string">'shopt -q login_shell && echo "Login shell" || echo "Not login shell"'</span>
    
    ```
    
    透過 SSH 執行遠端命令，預設將會跑在 **Non-Interactive** 模式下，但還是會依序載入 `/etc/bash.bashrc` --&gt; `~/.bashrc`
    
    
    - `Interactive?` 👉 `No`
    - `Login shell?` 👉 `No`
    
    如果你想在執行遠端命令時載入額外的啟動檔，也可以這樣寫：
    
    ```sh
    ssh user@host <span class="hljs-string">"source /etc/profile; source ~/.profile; /your/script.sh"</span>
    
    ```
6. 使用 SSH 執行遠端命令，透過 `bash` 呼叫另一個命令
    
    ```sh
    ssh user@host -t <span class="hljs-string">'bash -c '</span><span class="hljs-string">"'"</span><span class="hljs-string">'[ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"'</span><span class="hljs-string">"'"</span><span class="hljs-string">''</span>
    ssh user@host -t <span class="hljs-string">'bash -c '</span><span class="hljs-string">"'"</span><span class="hljs-string">'shopt -q login_shell && echo "Login shell" || echo "Not login shell"'</span><span class="hljs-string">"'"</span><span class="hljs-string">''</span>
    
    ```
    
    > 要在單引號(`'`)的字串中間加入一個單引號，必須輸入五個字元 `'"'"'` 才能代表一個單引號！（噁心的語法）
    
    > 在 ssh 執行時加入 `-t` 參數，可以在遠端執行時取得一個 pseudo-terminal allocation (`pts/0`)！
    
    透過 SSH 執行遠端命令，預設將會跑在 **Non-Interactive** 模式下，但還是會依序載入 `/etc/bash.bashrc` --&gt; `~/.bashrc`
    
    
    - `Interactive?` 👉 `No`
    - `Login shell?` 👉 `No`
    
    第二層 `bash` 應該是跑在 **Non-Interactive** 模式，但是卻依序載入 `/etc/bash.bashrc` --&gt; `~/.bashrc` 檔案，**這部分我還沒辦法理解為什麼會這樣**，感覺透過 SSH 執行遠端程式，預設就會載入這兩個檔案！
    
    
    - `Interactive?` 👉 `No`
    - `Login shell?` 👉 `No`
    
    底下這段是讓命令跑在 **Interactive** 模式，但載入啟動檔的順序竟然跟 **Non-Interactive** 竟然一樣！
    
    ```sh
    ssh user@host -t <span class="hljs-string">'bash -i -c '</span><span class="hljs-string">"'"</span><span class="hljs-string">'[ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"'</span><span class="hljs-string">"'"</span><span class="hljs-string">''</span>
    ssh user@host -t <span class="hljs-string">'bash -i -c '</span><span class="hljs-string">"'"</span><span class="hljs-string">'shopt -q login_shell && echo "Login shell" || echo "Not login shell"'</span><span class="hljs-string">"'"</span><span class="hljs-string">''</span>
    
    ```
    
    簡單來說，使用 SSH 直接遠端命令時，使用 `-i` (互動模式) 或不使用 `-i` 對載入啟動檔沒有什麼兩樣，不過都會載入兩次。不過還是有一個地方不同，如果你的**啟動檔**有設定 alias 的話，只有使用 `-i` 互動模式才能使用。
    
    例如以下這段命令，就會得到 `bash: ll: command not found` 的錯誤：
    
    ```sh
    ssh user@host -t <span class="hljs-string">'ll'</span>
    
    ```
    
    ```sh
    ssh user@host -t bash -c <span class="hljs-string">'ll'</span>
    
    ```
    
    如果改用 `-i` 來執行，就可以正常執行 `ll` 這個 alias 命令：
    
    ```sh
    ssh user@host -t bash -i -c <span class="hljs-string">'ll'</span>
    
    ```
7. 使用 SSH 執行遠端命令，並以 **Login shell** 啟動 Bash
    
    ```sh
    ssh user@host bash -l -c <span class="hljs-string">'set'</span>
    
    ```
    
    第一層 `bash` 依序載入 `/etc/bash.bashrc` --&gt; `~/.bashrc`
    
    第二層 `bash` 依序載入 `/etc/profile` --&gt; `~/.profile` \[ -&gt; `~/.bashrc` \]

# 【更新】Centos 7已於2024年6月30日停止維護，更換yum源解決yum404問題

```shell
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
mv /etc/yum.repos.d/epel.repo /etc/yum.repos.d/epel.repo.backup

curl -o /etc/yum.repos.d/CentOS-Base.repo \
http://mirrors.vpnforgame.net/centos/7/CentOS-Base.repo

curl -o /etc/yum.repos.d/epel.repo \
http://mirrors.vpnforgame.net/epel/7/epel.repo

yum clean all

yum makecache
```

# 【Linux】DNS 相關

### 如何知道本機dns server 設定在哪

```shell
# 查詢/etc/resolv.conf 
cat /etc/resolv.conf 

# ip 出現 類似nameserver 127.0.0.53
# 代表不設定在本機 使用resolvectl
resolvectl status

##########################################
Global
       Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub

Link 2 (eth0)
    Current Scopes: DNS
         Protocols: +DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 10.10.10.247
       DNS Servers: 10.10.10.246 10.10.10.247

```

# 【Linux】Dns 動態解析

若想實現同一個 DNS Server 根據不同來源 IP 返回不同的結果，這在 DNS 中稱為**視圖（View）或基於來源的策略解析**，以下是詳細實現方式。

---

### 使用 Bind 實現（最佳選擇）

Bind 支持基於來源 IP 的視圖配置，可以為不同來源的請求返回不同的 DNS 記錄。

#### 配置步驟

1. **安裝 Bind** 安裝 Bind DNS Server：
    
    ```
    sudo apt install <span class="hljs-built_in">bind</span>9
    
    ```
2. **配置視圖（View）** 編輯 Bind 的主配置文件 `/etc/bind/named.conf`，添加基於來源 IP 的視圖。
    
    範例配置：
    
    ```
    acl <span class="hljs-string">"internal-network"</span> {
        <span class="hljs-number">192.168</span>.<span class="hljs-number">1.0</span>/<span class="hljs-number">24</span><span class="hljs-comment">;</span>
    }<span class="hljs-comment">;</span>
    
    acl <span class="hljs-string">"external-network"</span> {
        any<span class="hljs-comment">;</span>
    }<span class="hljs-comment">;</span>
    
    view <span class="hljs-string">"internal"</span> {
        match-clients { <span class="hljs-string">"internal-network"</span><span class="hljs-comment">; };</span>
        zone <span class="hljs-string">"example.com"</span> {
            type master<span class="hljs-comment">;</span>
            file <span class="hljs-string">"/etc/bind/db.internal.example.com"</span><span class="hljs-comment">;</span>
        }<span class="hljs-comment">;</span>
    }<span class="hljs-comment">;</span>
    
    view <span class="hljs-string">"external"</span> {
        match-clients { <span class="hljs-string">"external-network"</span><span class="hljs-comment">; };</span>
        zone <span class="hljs-string">"example.com"</span> {
            type master<span class="hljs-comment">;</span>
            file <span class="hljs-string">"/etc/bind/db.external.example.com"</span><span class="hljs-comment">;</span>
        }<span class="hljs-comment">;</span>
    }<span class="hljs-comment">;</span>
    
    ```
3. **創建區域文件** 根據來源 IP 創建不同的區域文件。例如：
    
    
    - **內部網絡的區域文件 `/etc/bind/db.internal.example.com`**：
        
        ```
        <span class="hljs-meta">$TTL</span> <span class="hljs-number">604800</span>
        @   <span class="hljs-keyword">IN</span>  <span class="hljs-keyword">SOA</span> example.com. admin.example.com. (
                <span class="hljs-number">1</span>   <span class="hljs-comment">; Serial</span>
                <span class="hljs-number">604800</span> <span class="hljs-comment">; Refresh</span>
                <span class="hljs-number">86400</span> <span class="hljs-comment">; Retry</span>
                <span class="hljs-number">2419200</span> <span class="hljs-comment">; Expire</span>
                <span class="hljs-number">604800</span> ) <span class="hljs-comment">; Negative Cache TTL</span>
        <span class="hljs-comment">;</span>
        @   <span class="hljs-keyword">IN</span>  <span class="hljs-keyword">NS</span>  ns.example.com.
        @   <span class="hljs-keyword">IN</span>  <span class="hljs-keyword">A</span>   <span class="hljs-number">192.168.1.1</span>
        
        ```
    - **外部網絡的區域文件 `/etc/bind/db.external.example.com`**：
        
        ```
        <span class="hljs-meta">$TTL</span> <span class="hljs-number">604800</span>
        @   <span class="hljs-keyword">IN</span>  <span class="hljs-keyword">SOA</span> example.com. admin.example.com. (
                <span class="hljs-number">1</span>   <span class="hljs-comment">; Serial</span>
                <span class="hljs-number">604800</span> <span class="hljs-comment">; Refresh</span>
                <span class="hljs-number">86400</span> <span class="hljs-comment">; Retry</span>
                <span class="hljs-number">2419200</span> <span class="hljs-comment">; Expire</span>
                <span class="hljs-number">604800</span> ) <span class="hljs-comment">; Negative Cache TTL</span>
        <span class="hljs-comment">;</span>
        @   <span class="hljs-keyword">IN</span>  <span class="hljs-keyword">NS</span>  ns.example.com.
        @   <span class="hljs-keyword">IN</span>  <span class="hljs-keyword">A</span>   <span class="hljs-number">203.0.113.1</span>
        
        ```
4. **重啟 Bind** 重啟 Bind 服務：
    
    ```
    sudo systemctl restart <span class="hljs-built_in">bind</span>9
    
    ```

---

### 使用 `dnsmasq` 實現（輕量級）

#### 配置步驟

1. **安裝 dnsmasq**
    
    ```
    sudo apt <span class="hljs-keyword">install</span> dnsmasq
    
    ```
2. **配置條件解析** 編輯 `/etc/dnsmasq.conf`，添加基於來源 IP 的條件解析規則：
    
    ```
    # 為內部網絡配置
    dhcp-range=set:internal,<span class="hljs-number">192.168</span><span class="hljs-meta">.1</span><span class="hljs-meta">.0</span>,<span class="hljs-number">192.168</span><span class="hljs-meta">.1</span><span class="hljs-meta">.255</span>
    address=/example.com/<span class="hljs-number">192.168</span><span class="hljs-meta">.1</span><span class="hljs-meta">.1</span>
    
    # 為外部網絡配置
    dhcp-range=set:external,<span class="hljs-number">0.0</span><span class="hljs-meta">.0</span><span class="hljs-meta">.0</span>,<span class="hljs-number">255.255</span><span class="hljs-meta">.255</span><span class="hljs-meta">.255</span>
    address=/example.com/<span class="hljs-number">203.0</span><span class="hljs-meta">.113</span><span class="hljs-meta">.1</span>
    
    ```
3. **重啟 dnsmasq**
    
    ```
    <span class="hljs-attribute">sudo systemctl restart dnsmasq</span>
    
    ```

---

### 使用 PowerDNS（動態解析）

- [https://wiki.freedomstu.com/books/%E9%96%8B%E6%BA%90%E8%BB%9F%E9%AB%94%E5%AE%89%E8%A3%9D%E6%B5%81%E7%A8%8B/page/powerdns-debian](https://wiki.freedomstu.com/books/%E9%96%8B%E6%BA%90%E8%BB%9F%E9%AB%94%E5%AE%89%E8%A3%9D%E6%B5%81%E7%A8%8B/page/powerdns-debian)
- [https://doc.powerdns.com/authoritative/indexTOC.html](https://doc.powerdns.com/authoritative/indexTOC.html)
- [https://blog.csdn.net/sdhzdtwhm/article/details/135910057](https://blog.csdn.net/sdhzdtwhm/article/details/135910057)
- [https://lantian.pub/article/modify-website/powerdns-lua-diy-geodns.lantian/](https://lantian.pub/article/modify-website/powerdns-lua-diy-geodns.lantian/)
- [https://github.com/PowerDNS/pdns/blob/master/docker-compose.yml](https://github.com/PowerDNS/pdns/blob/master/docker-compose.yml)

#### 配置 PowerDNS + Lua 脚本

PowerDNS 支持使用 Lua 腳本進行動態解析。

1. **安裝 PowerDNS 和相關組件**
    
    ```
    sudo apt <span class="hljs-keyword">install</span> pdns-<span class="hljs-keyword">server</span> pdns-backend-sqlite3
    
    ```
2. **啟用 Lua 腳本支持** 在 `/etc/powerdns/pdns.conf` 中啟用 Lua Backend：
    
    ```
    <span class="hljs-attr">launch</span>=gsqlite3,lua
    <span class="hljs-attr">lua-dns-script</span>=/etc/powerdns/dns.lua
    
    ```
3. **編寫 Lua 腳本** 創建 `/etc/powerdns/dns.lua`，根據來源 IP 返回不同的結果：
    
    ```
    <span class="hljs-keyword">function</span> postresolve(remoteip, domain, qtype)
        <span class="hljs-keyword">if</span> string.match(remoteip, <span class="hljs-string">"^192%.168%.1%."</span>) <span class="hljs-keyword">then</span>
            <span class="hljs-keyword">return</span> {{qtype=<span class="hljs-string">"A"</span>, content=<span class="hljs-string">"192.168.1.1"</span>, ttl=<span class="hljs-number">60</span>}}
        <span class="hljs-keyword">else</span>
            <span class="hljs-keyword">return</span> {{qtype=<span class="hljs-string">"A"</span>, content=<span class="hljs-string">"203.0.113.1"</span>, ttl=<span class="hljs-number">60</span>}}
        <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>
    
    ```
4. **重啟 PowerDNS**
    
    ```
    <span class="hljs-attribute">sudo systemctl restart pdns</span>
    
    ```

---

### 測試

#### 使用 `dig` 測試

針對不同的來源 IP 測試返回的解析結果。

- 從內部網絡：
    
    ```
    <span class="hljs-keyword">dig</span> @<span class="hljs-symbol"><DNS_SERVER_IP></span> example.<span class="hljs-keyword">com</span>
    
    ```
    
    返回內部地址（例如 `192.168.1.1`）。
- 從外部網絡：
    
    ```
    <span class="hljs-keyword">dig</span> @<span class="hljs-symbol"><DNS_SERVER_IP></span> example.<span class="hljs-keyword">com</span>
    
    ```
    
    返回外部地址（例如 `203.0.113.1`）。

---

### 注意事項

1. **來源 IP 檢測**：
    
    
    - 檢查網絡路由，確保 DNS 伺服器能正確檢測請求的來源 IP。
    - 若有 NAT 設置，需配置正確的來源地址。
2. **DNS 緩存**：
    
    
    - 若存在 DNS 緩存伺服器，可能會干擾條件解析結果。
3. **性能**：
    
    
    - `Bind` 和 `PowerDNS` 適合高流量環境。
    - `dnsmasq` 適合輕量級應用場景。

這些方案可以靈活地根據來源 IP 實現不同的 DNS 解析結果。

# 【Linux】【權限管理】【acl】設定/var/www/html for tomcat 可讀寫

以下是如何在 Ubuntu 中設置這些 ACL 權限的步驟。

---

### **步驟 1：啟用 ACL 支持**

如果 ACL 尚未啟用，請確保相關功能已經安裝並啟用：

1. 安裝 ACL 工具： ```
    sudo apt <span class="hljs-keyword">install</span> acl
    
    ```
2. 確保文件系統支持 ACL： ```
    <span class="hljs-attribute">sudo</span> mount -o remount,acl /
    
    ```

---

### **步驟 2：設置基本權限**

設定 `/var/www/html` 的基本權限：

```lang-bash
sudo chmod 775 /var/www/html
sudo chown root:root /var/www/html

```

---

### **步驟 3：配置 ACL 權限**

根據需求逐步配置 ACL：

1. **設置具體使用者和群組的 ACL 權限**：
    
    ```
    sudo setfacl -m <span class="hljs-string">u:</span><span class="hljs-string">tomcat:</span>rwx <span class="hljs-regexp">/var/</span>www/html
    sudo setfacl -m <span class="hljs-string">g:</span>:r-x <span class="hljs-regexp">/var/</span>www/html
    sudo setfacl -m <span class="hljs-string">o:</span>:r-x <span class="hljs-regexp">/var/</span>www/html
    
    ```
    
    
    - **`u:tomcat:rwx`**：給 `tomcat` 用戶設置 `rwx` 權限。
    - **`g::r-x`**：默認組的權限為 `r-x`。
    - **`o::r-x`**：其他人的權限為 `r-x`。
2. **設置默認 ACL（適用於未來子目錄和文件）**：
    
    ```
    sudo setfacl -d -m <span class="hljs-string">u:</span>:rwx <span class="hljs-regexp">/var/</span>www/html
    sudo setfacl -d -m <span class="hljs-string">u:</span><span class="hljs-string">tomcat:</span>rwx <span class="hljs-regexp">/var/</span>www/html
    sudo setfacl -d -m <span class="hljs-string">g:</span>:r-x <span class="hljs-regexp">/var/</span>www/html
    sudo setfacl -d -m <span class="hljs-string">o:</span>:r-x <span class="hljs-regexp">/var/</span>www/html
    
    ```
    
    
    - **`-d`**：表示設置默認 ACL（對新創建的文件/目錄生效）。

---

### **步驟 4：檢查 ACL 設置**

使用以下指令檢查 ACL 配置是否正確：

```lang-bash
getfacl /var/www/html

```

輸出應類似於：

```
# file: var/www/html
# owner: root
# group: root
user::rwx
user:tomcat:rwx
group::r-x
mask::rwx
other::r-x
default:user::rwx
default:user:tomcat:rwx
default:group::r-x
default:mask::rwx
default:other::r-x

```

---

### **ACL 設置的解釋**

1. **普通權限**：
    
    
    - **`user::rwx`**：目錄擁有者 `root` 的權限。
    - **`user:tomcat:rwx`**：為用戶 `tomcat` 設置的權限。
    - **`group::r-x`**：目錄的群組成員的權限。
    - **`other::r-x`**：其他用戶的權限。
    - **`mask::rwx`**：ACL 中權限的上限（最高可設置為 `rwx`）。
2. **默認權限（`default:` 開頭）**：
    
    
    - **`default:user::rwx`**：默認擁有者的權限。
    - **`default:user:tomcat:rwx`**：默認為 `tomcat` 設置的權限。
    - **`default:group::r-x`**：默認群組的權限。
    - **`default:other::r-x`**：默認其他用戶的權限。

---

### **總結指令**

以下是一鍵完成配置的指令集合：

```lang-bash
sudo setfacl -m u:tomcat:rwx /var/www/html
sudo setfacl -m g::r-x /var/www/html
sudo setfacl -m o::r-x /var/www/html
sudo setfacl -d -m u::rwx /var/www/html
sudo setfacl -d -m u:tomcat:rwx /var/www/html
sudo setfacl -d -m g::r-x /var/www/html
sudo setfacl -d -m o::r-x /var/www/html

```

執行後確認權限是否正確，應與您列出的設定一致！

---

是否需要設定 `-d` 取決於您的需求，以下是有關 **默認 ACL（`-d`）** 的詳細說明以及什麼情況下需要設定。

---

### **什麼是默認 ACL（`-d`）？**

默認 ACL 是一組規則，會自動應用到 **新創建的文件或子目錄** 中。當一個目錄設置了默認 ACL，該目錄下創建的任何新文件或目錄都會繼承這些默認規則。

---

### **是否需要設定 `-d`**

1. **需要設定 `-d` 的情況**
    
    
    - 如果您希望 **新創建的文件或子目錄** 自動繼承特定的 ACL 設置（如 `tomcat` 擁有特定權限），那麼您需要設置默認 ACL。例如： 
        - `/var/www/html` 是一個共享的目錄，將會持續新增文件。
        - 您希望 `tomcat` 用戶對新增的文件有 **`rwx`** 權限。
    
    ```
    sudo setfacl -d -m <span class="hljs-string">u:</span><span class="hljs-string">tomcat:</span>rwx <span class="hljs-regexp">/var/</span>www/html
    
    ```
    
    當某人創建一個新文件時，`tomcat` 就會自動擁有該文件的權限。
2. **不需要設定 `-d` 的情況**
    
    
    - 如果您僅關心目錄 **當前的文件和目錄**，而不需要未來的新增文件繼承權限。
    - 如果您會手動或通過腳本為新文件設置權限，而不依賴默認 ACL。
    
    在這種情況下，您可以省略 `-d`，只設置當前的 ACL 即可：
    
    ```
    sudo setfacl -m <span class="hljs-string">u:</span><span class="hljs-string">tomcat:</span>rwx <span class="hljs-regexp">/var/</span>www/html
    
    ```

---

### **範例：對比是否設置 `-d` 的效果**

假設您在 `/var/www/html` 下創建了兩個文件：

1. **沒有設置 `-d` 的情況**
    
    
    - 當您創建新文件時，不會繼承 `tomcat` 的權限。
    - 新文件的權限取決於當前的系統默認值（如 `umask`）。
2. **設置了 `-d` 的情況**
    
    
    - 新文件或目錄將繼承默認 ACL。例如，`tomcat` 將擁有對新文件的權限： ```
        touch /<span class="hljs-keyword">var</span>/www/html/<span class="hljs-keyword">new</span><span class="hljs-type">file</span>
        getfacl /<span class="hljs-keyword">var</span>/www/html/<span class="hljs-keyword">new</span><span class="hljs-type">file</span>
        
        ```
        
        結果顯示 `tomcat` 的權限被自動應用。

---

### **結論**

- **需要持續新增文件/目錄並繼承權限**：建議使用 `-d` 設置默認 ACL。
- **僅對當前文件和目錄設置權限**：可以省略 `-d`。

建議根據場景選擇適合的設置，以滿足項目需求。

# 【Linux】SSH 跳板機設定

如果你經常需要透過主機A跳到主機B進行操作，可以考慮以下幾種方法來提升效率：

### 1. **SSH 跳板機設定 (ProxyJump 或 ProxyCommand)**

你可以在 `~/.ssh/config` 檔案中設定跳板機 (主機A) 和目標主機 (主機B)。

#### 方法一：使用 ProxyJump (推薦，簡潔且較新)

```lang-bash
   Host 主機B
       HostName <主機B的IP或主機名>
       User <使用者名稱>
       ProxyJump <主機A的使用者名稱>@<主機A的IP或主機名>

```

使用這個設定後，你可以直接用 `ssh 主機B` 連接到主機B，不需先登入主機A。

#### 方法二：使用 ProxyCommand (舊版本支援)

```lang-bash
   Host 主機B
       HostName <主機B的IP或主機名>
       User <使用者名稱>
       ProxyCommand ssh <主機A的使用者名稱>@<主機A的IP> -W %h:%p

```

這個設定同樣讓你直接 `ssh 主機B` 連接到主機B。

---

### 2. **SSH 快速別名設定 (使用別名簡化命令)**

你也可以在 `~/.bashrc` 或 `~/.zshrc` 中設定別名，直接執行一個指令來登入主機B。

```lang-bash
alias sshB='ssh -J <主機A的使用者名稱>@<主機A的IP或主機名> <主機B的使用者名稱>@<主機B的IP或主機名>'

```

之後，你只需要輸入 `sshB` 就能登入主機B。

---

### 3. **使用 SSH Multiplexing (重複利用 SSH 連線)**

如果你需要頻繁連線到同一個主機，可以開啟 SSH 連線複用功能，減少重複登入的時間：

在 `~/.ssh/config` 加入以下內容：

```lang-bash
Host *
    ControlMaster auto
    ControlPath ~/.ssh/control-%r@%h:%p
    ControlPersist 10m

```

這樣在第一次登入主機A時，SSH 會保持連線，之後登入主機B就會變得更快。

---

### 4. **使用 SSH Agent 轉發 (Agent Forwarding)**

如果主機A和主機B需要同一組 SSH 金鑰，可以使用 SSH Agent Forwarding，避免在主機A上存放私鑰：

```lang-bash
ssh -A <主機A的使用者名稱>@<主機A的IP>
ssh <主機B的使用者名稱>@<主機B的IP>

```

或者在 `~/.ssh/config` 啟用 Agent Forwarding：

```lang-bash
Host 主機A
    ForwardAgent yes

```

這樣就能直接將本地端的 SSH 金鑰轉發到主機A，並用來登入主機B。

---

### 5. **使用 `tmux` 或 `screen` (維持長時間工作環境)**

如果你的操作過程需要持續很長時間，建議在主機A或B上使用 `tmux` 或 `screen` 來保持工作環境，這樣即使斷線也能恢復。

#### 安裝並啟動 tmux：

```lang-bash
tmux new -s session_name

```

之後可以斷開連線並重新附加：

```lang-bash
tmux attach -t session_name

```

---

### 6. **使用 Ansible 自動化操作 (進階)**

如果你有固定的操作流程，可以使用 Ansible 寫 Playbook，批量操作多個主機，包括跳板機情境：

```lang-yaml
- name: Jump to 主機B via 主機A
  hosts: 主機B
  vars:
    ansible_ssh_common_args: '-o ProxyJump=<主機A的使用者名稱>@<主機A的IP>'
  tasks:
    - name: Run shell command
      command: <你的shell指令>

```

使用 Ansible 來批量執行，避免手動登入和操作。

---

這幾個方法可以根據需求選擇，**ProxyJump** 和 **SSH Config** 設定最為方便、簡潔，推薦優先使用！

# 【Tool】tmux

## 📦 **tmux 簡易說明**

**tmux**（Terminal Multiplexer）是一個強大的終端複用工具，可以在單一終端視窗中同時管理多個工作階段（sessions）、視窗（windows）、與窗格（panes）。

---

## 🚀 **tmux 的核心概念**

1. **Session（工作階段）**：
    
    
    - 一個 tmux session 可以包含多個視窗，像是一個獨立的工作空間。
    - 支援在背景運行，即使關閉終端也不會中斷。
    - [![image-1739418422966.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-02/scaled-1680-/J89JzJ2i8N2ZANS2-image-1739418422966.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-02/J89JzJ2i8N2ZANS2-image-1739418422966.png)
2. **Window（視窗）**：
    
    
    - 每個 session 裡可以有多個視窗，每個視窗類似一個完整的終端畫面。
    - 視窗可分割成多個窗格，方便同時監控多個任務。
    - [![image-1739418697038.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-02/scaled-1680-/tvyuE5Dk3fsIvmmC-image-1739418697038.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-02/tvyuE5Dk3fsIvmmC-image-1739418697038.png)
3. **Pane（窗格）**：
    
    
    - 視窗內的分割區域，每個窗格可獨立執行命令。
    - 支援水平與垂直分割，方便進行多任務操作。
    - [![image-1739418735165.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-02/scaled-1680-/dzCssSO2N4l19WC7-image-1739418735165.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-02/dzCssSO2N4l19WC7-image-1739418735165.png)

---

## **安裝 tmux**

##### **Ubuntu 安裝 tmux**

Ubuntu 官方倉庫提供了 tmux，可直接使用 `apt` 安裝：

```bash
sudo apt update
sudo apt install tmux -y

```

---

##### **macOS 安裝 tmux**

macOS 用戶可以透過 **Homebrew** 安裝：

```bash
brew install tmux

```

---

##### 🔍 **更新或移除 tmux**

##### **Ubuntu**

- **更新**： ```bash
    sudo apt update && sudo apt upgrade tmux
    
    ```
- **移除**： ```bash
    sudo apt remove tmux -y
    
    ```

##### **macOS**

- **更新**： ```bash
    brew upgrade tmux
    
    ```
- **移除**： ```bash
    brew uninstall tmux
    
    ```

---

### ⌨️ **常用指令快速上手**

<table id="bkmrk-%E5%8A%9F%E8%83%BD-%E6%8C%87%E4%BB%A4-%2F-%E5%BF%AB%E6%8D%B7%E9%8D%B5-%E8%AA%AA%E6%98%8E-%E5%95%9F%E5%8B%95-tm" style="width:866px;height:1197px;"><thead><tr style="height:29px;"><th style="width:221px;height:29px;">功能</th><th style="width:216px;height:29px;">指令 / 快捷鍵</th><th style="width:429px;height:29px;">說明</th></tr></thead><tbody><tr style="height:29px;"><td class="align-center" colspan="3" style="width:866px;height:29px;"> **session**</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**啟動 tmux**</td><td style="width:216px;height:29px;">`tmux`</td><td style="width:429px;height:29px;">開啟一個新的 tmux session</td></tr><tr style="height:46px;"><td style="width:221px;height:46px;"> </td><td style="width:216px;height:46px;">`tmux new -s 名稱` </td><td style="width:429px;height:46px;">開啟一個新的 tmux session 使用自訂名稱</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**查看所有 session**</td><td style="width:216px;height:29px;">`tmux ls`</td><td style="width:429px;height:29px;">列出目前所有 tmux session</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**切換 session**</td><td style="width:216px;height:29px;">`Ctrl-b s`</td><td style="width:429px;height:29px;">圖形化切換session

(如果該session 內有多視窗，可按 -&gt; 選擇換到該視窗)

</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**離開 tmux（保持運行）**</td><td style="width:216px;height:29px;">`Ctrl-b d`</td><td style="width:429px;height:29px;">分離 session，回到原本終端</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**離開 tmux（結束此session）**</td><td style="width:216px;height:29px;">`Ctrl-b x`</td><td style="width:429px;height:29px;">結束目前session，回到原本終端</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**重新連接 session**</td><td style="width:216px;height:29px;">`tmux attach -t 名稱`</td><td style="width:429px;height:29px;">重新連接到未結束的 session</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;"> </td><td style="width:216px;height:29px;">`tmux a` </td><td style="width:429px;height:29px;">重新連接上次中斷seeion</td></tr><tr style="height:33px;"><td style="width:221px;height:33px;">**刪除 session** </td><td style="width:216px;height:33px;">`tmux kill-session -t 名稱`

</td><td style="width:429px;height:33px;">刪除指定名稱session</td></tr><tr style="height:36px;"><td style="width:221px;height:36px;">**刪除所有 session** </td><td style="width:216px;height:36px;">`<span class="s1">tmux kill-server</span>`

</td><td style="width:429px;height:36px;"> </td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**重新命名 session**</td><td style="width:216px;height:29px;">`Ctrl-b $`</td><td style="width:429px;height:29px;">當前session 改名</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;"> </td><td style="width:216px;height:29px;">`tmux rename-session -t 舊名稱 新名稱`</td><td style="width:429px;height:29px;">修改指令 session 名稱</td></tr><tr style="height:29px;"><td class="align-center" colspan="3" style="width:866px;height:29px;">**window**</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**建立新視窗**</td><td style="width:216px;height:29px;">`Ctrl-b c`</td><td style="width:429px;height:29px;">建立一個新的視窗</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**切換視窗**</td><td style="width:216px;height:29px;">`Ctrl-b w`</td><td style="width:429px;height:29px;">圖形化切換window</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;"> </td><td style="width:216px;height:29px;">`Ctrl-b 視窗編號`</td><td style="width:429px;height:29px;"> </td></tr><tr style="height:29px;"><td style="width:221px;height:29px;"> </td><td style="width:216px;height:29px;">`Ctrl-b n / p`</td><td style="width:429px;height:29px;">切換到下一個/上一個視窗</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**關閉視窗**</td><td style="width:216px;height:29px;">`Ctrl-b &`</td><td style="width:429px;height:29px;">關閉當前視窗</td></tr><tr style="height:30px;"><td style="width:221px;height:30px;">**重新命名 window**</td><td style="width:216px;height:30px;">`Ctrl-b ,`</td><td style="width:429px;height:30px;">更名目前window</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;"> </td><td style="width:216px;height:29px;">`tmux rename-window -t 舊名稱 新名稱`</td><td style="width:429px;height:29px;">更名指定window</td></tr><tr style="height:29px;"><td class="align-center" colspan="3" style="width:866px;height:29px;">**Pane**</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**分割窗格（垂直）**</td><td style="width:216px;height:29px;">`Ctrl-b "`</td><td style="width:429px;height:29px;">垂直分割出新窗格</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**分割窗格（水平）**</td><td style="width:216px;height:29px;">`Ctrl-b %`</td><td style="width:429px;height:29px;">水平分割出新窗格</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**切換窗格**</td><td style="width:216px;height:29px;">`Ctrl-b 方向鍵`</td><td style="width:429px;height:29px;">在窗格間切換</td></tr><tr style="height:57px;"><td style="width:221px;height:57px;"> </td><td style="width:216px;height:57px;">vi ~/.tmux.conf   
加入set -g mouse on</td><td style="width:429px;height:57px;">**使用滑鼠切換**

**使用滑鼠調整分割線**

</td></tr><tr style="height:35px;"><td style="width:221px;height:35px;">**關閉窗格**</td><td style="width:216px;height:35px;">`Ctrl-b x`</td><td style="width:429px;height:35px;"></td></tr><tr style="height:35px;"><td style="width:221px;height:35px;">**與上一格窗格互換**</td><td style="width:216px;height:35px;">`Ctrl-b {`</td><td style="width:429px;height:35px;"></td></tr><tr style="height:35px;"><td style="width:221px;height:35px;">**與下一格窗格互換**</td><td style="width:216px;height:35px;">`Ctrl-b }`</td><td style="width:429px;height:35px;"></td></tr><tr style="height:35px;"><td style="width:221px;height:35px;">**顯示窗格編號**</td><td style="width:216px;height:35px;">`Ctrl-b q`</td><td style="width:429px;"></td></tr><tr style="height:35px;"><td style="width:221px;height:35px;">**指定窗格互換**</td><td style="width:216px;height:35px;">`tmux swap-pane -s 1 -t 2`</td><td style="width:429px;height:35px;"></td></tr><tr style="height:29px;"><td class="align-center" colspan="3" style="width:866px;height:29px;">**其他**</td></tr><tr style="height:29px;"><td style="width:221px;height:29px;">**命令模式**</td><td style="width:216px;height:29px;">`Ctrl-b :`</td><td style="width:429px;height:29px;">進入命令模式，可執行 tmux 指令</td></tr><tr style="height:124px;"><td style="width:221px;height:124px;">**配置設定檔**</td><td style="width:216px;height:124px;">vi ~/.tmux.conf

set -g mouse on

:支持滑鼠

set -g prefix C-s

:ctrl + b前缀改成ctrl + s

</td><td style="width:429px;height:124px;"> </td></tr></tbody></table>

當滑鼠模式關閉(set -g mouse off)時，作業系統提供的標準複製/貼上功能將如預期運作。  
但<span style="background-color:#ffff99;">滑鼠模式開啟</span>(set -g mouse on)時，<span style="background-color:#ff99cc;">剪貼簿的功能會造成失效</span>  
下面的程式碼可以新增到您的 .tmux.conf 檔案中，以將m和M鍵分別綁定到「滑鼠開啟」和「滑鼠關閉」。

這讓你能夠使用Ctrl+ B,m來開啟滑鼠支援；Ctrl+ B，M將其關閉。

```shell
vi ~/.tmux.conf



###############################################
# toggle mouse mode to allow mouse copy/paste
# set mouse on with prefix m
bind m \
    set -g mouse on \;\
    display 'Mouse: ON'
# set mouse off with prefix M
bind M \
    set -g mouse off \;\
    display 'Mouse: OFF'        
```

參考:[https://stackoverflow.com/questions/17445100/getting-back-old-copy-paste-behaviour-in-tmux-with-mouse](https://stackoverflow.com/questions/17445100/getting-back-old-copy-paste-behaviour-in-tmux-with-mouse)

---

### 🎯 **常見使用情境**

- **遠端連線不中斷：** 即使 SSH 斷線，tmux session 仍會在伺服器上持續運行。
- **多任務同時進行：** 在一個終端視窗內同時監控多個任務、Log 或服務狀態。
- **自動化腳本執行：** 方便長時間執行的任務或背景作業管理。

---

### **常見問題**

**已在某session 內要新建session**

[![image-1739416150135.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-02/scaled-1680-/Lkmr5LdaNqnKwgDJ-image-1739416150135.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-02/Lkmr5LdaNqnKwgDJ-image-1739416150135.png)

或是要切換到另一個session

[![image-1739416354391.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-02/scaled-1680-/rxWRa14AD0D59S5X-image-1739416354391.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-02/rxWRa14AD0D59S5X-image-1739416354391.png)

這代表 **你已經在一個 tmux session 裡，然後又試圖附加（attach）到另一個 session**。tmux 預設不建議這種「嵌套 session」的行為，因為可能會導致控制鍵衝突或混亂。

建議方式：

**建立session**:離開tmux 模式`Ctrl-b d`，在建立新的session : `tmux` or `tmux new -s 名稱`

**切換session**: `Ctrl-b s` 切換 or `tmux switch-client -t 名稱`

[![image-1739416638882.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-02/scaled-1680-/ImlgX07xgcgQxTnB-image-1739416638882.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-02/ImlgX07xgcgQxTnB-image-1739416638882.png)

---

以下是 tmux 快速鍵表格：

<table id="bkmrk-%E5%BF%AB%E9%80%9F%E9%8D%B5-%E5%8A%9F%E8%83%BD%E8%AA%AA%E6%98%8E-c-b-space-%E9%81%B8"><thead><tr><th>快速鍵</th><th>功能說明</th></tr></thead><tbody><tr><td>C-b Space</td><td>選擇下一個佈局</td></tr><tr><td>C-b !</td><td>將窗格分離到新視窗</td></tr><tr><td>C-b "</td><td>垂直分割視窗</td></tr><tr><td>C-b #</td><td>列出所有剪貼簿</td></tr><tr><td>C-b $</td><td>重新命名當前工作階段</td></tr><tr><td>C-b %</td><td>水平分割視窗</td></tr><tr><td>C-b &amp;</td><td>關閉當前視窗</td></tr><tr><td>C-b '</td><td>輸入視窗索引以切換</td></tr><tr><td>C-b (</td><td>切換到上一個客戶端</td></tr><tr><td>C-b )</td><td>切換到下一個客戶端</td></tr><tr><td>C-b ,</td><td>重新命名當前視窗</td></tr><tr><td>C-b -</td><td>刪除最近的剪貼簿</td></tr><tr><td>C-b .</td><td>移動當前視窗</td></tr><tr><td>C-b /</td><td>顯示按鍵綁定說明</td></tr><tr><td>C-b 0~9</td><td>選擇對應編號的視窗</td></tr><tr><td>C-b :</td><td>輸入命令提示符</td></tr><tr><td>C-b ;</td><td>切換到上次使用的窗格</td></tr><tr><td>C-b =</td><td>從清單中選擇剪貼簿</td></tr><tr><td>C-b ?</td><td>列出所有按鍵綁定</td></tr><tr><td>C-b C</td><td>自訂選項</td></tr><tr><td>C-b D</td><td>從清單中選擇並分離客戶端</td></tr><tr><td>C-b E</td><td>平均分配所有窗格</td></tr><tr><td>C-b L</td><td>切換到最後一個客戶端</td></tr><tr><td>C-b M</td><td>清除標記的窗格</td></tr><tr><td>C-b \[</td><td>進入複製模式</td></tr><tr><td>C-b \]</td><td>貼上最近的剪貼簿</td></tr><tr><td>C-b c</td><td>建立新視窗</td></tr><tr><td>C-b d</td><td>分離當前客戶端</td></tr><tr><td>C-b f</td><td>搜尋窗格</td></tr><tr><td>C-b i</td><td>顯示視窗資訊</td></tr><tr><td>C-b l</td><td>切換到上個視窗</td></tr><tr><td>C-b m</td><td>標記或取消標記窗格</td></tr><tr><td>C-b n</td><td>選擇下一個視窗</td></tr><tr><td>C-b o</td><td>選擇下一個窗格</td></tr><tr><td>C-b p</td><td>選擇上一個視窗</td></tr><tr><td>C-b q</td><td>顯示窗格編號</td></tr><tr><td>C-b r</td><td>重新整理當前客戶端</td></tr><tr><td>C-b s</td><td>從清單中選擇工作階段</td></tr><tr><td>C-b t</td><td>顯示時鐘</td></tr><tr><td>C-b w</td><td>從清單中選擇視窗</td></tr><tr><td>C-b x</td><td>關閉當前窗格</td></tr><tr><td>C-b z</td><td>放大/還原當前窗格</td></tr><tr><td>C-b {</td><td>與上方窗格交換位置</td></tr><tr><td>C-b }</td><td>與下方窗格交換位置</td></tr><tr><td>C-b ~</td><td>顯示訊息</td></tr><tr><td>C-b DC</td><td>重置視窗以跟隨游標</td></tr><tr><td>C-b PPage</td><td>進入複製模式並向上捲動</td></tr><tr><td>C-b ↑/↓/←/→</td><td>選擇相對方向的窗格</td></tr><tr><td>C-b M-1~M-7</td><td>設定不同的視窗佈局</td></tr><tr><td>C-b M-n</td><td>選擇下一個有警示的視窗</td></tr><tr><td>C-b M-o</td><td>反向循環切換窗格</td></tr><tr><td>C-b M-p</td><td>選擇上一個有警示的視窗</td></tr><tr><td>C-b M-↑/↓/←/→</td><td>調整窗格大小 (每次5個單位)</td></tr><tr><td>C-b C-b</td><td>傳送前綴鍵</td></tr><tr><td>C-b C-o</td><td>循環切換窗格</td></tr><tr><td>C-b C-z</td><td>暫停當前客戶端</td></tr><tr><td>C-b C-↑/↓/←/→</td><td>調整窗格大小</td></tr><tr><td>C-b S-↑/↓/←/→</td><td>移動視窗的可見部分</td></tr></tbody></table>

Reference

- [tmux 小記](https://sakananote2.blogspot.com/2025/01/tmux.html)
- [<span>tmux 使用和基礎配置 從入門到加班 一個視頻全搞定！</span>](https://www.youtube.com/watch?v=QGnjSccjDck&ab_channel=%E7%A8%8B%E5%BA%8F%E5%91%98%E8%80%81%E7%8E%8B)
- [https://hackmd.io/@Cheng-Hao/Hyk9f6mZd](https://hackmd.io/@Cheng-Hao/Hyk9f6mZd)
- [https://blog.csdn.net/qq\_41634814/article/details/119426093](https://blog.csdn.net/qq_41634814/article/details/119426093)
- [<span>you need to learn tmux RIGHT NOW!!</span>](https://youtu.be/nTqu6w2wc68?si=f_4pe7lmm3Ax1pan)

# 【Linux】【Ubuntu】 中更改 hostname

在 Ubuntu 中更改 **hostname** 有兩種常見方式，以下提供完整步驟：

---

## 1️⃣ 使用 `hostnamectl`（推薦方式，Ubuntu 16.04 之後）

`hostnamectl` 是 systemd 提供的指令，可同時修改 *暫時* 與 *永久* 主機名稱。

```bash
# 查看當前 hostname
hostnamectl status

# 修改 hostname，例如改成 my-server
sudo hostnamectl set-hostname my-server

```

- **立即生效**：不需重開機，新的主機名稱會寫入 `/etc/hostname` 並更新 `systemd`。
- 驗證：
    
    ```bash
    hostname
    hostnamectl
    
    ```

> ⚠️ 有時候 shell 提示字仍顯示舊名稱，重新登入 SSH 或 `su - $USER` 會看到更新。

---

## 2️⃣ 手動編輯設定檔

如果是較舊版本或想手動修改，可直接編輯以下兩個檔案：

1. **/etc/hostname**
    
    ```bash
    sudo nano /etc/hostname
    
    ```
    
    把原本的名稱改成新的，如 `my-server`。
2. **/etc/hosts**
    
    ```bash
    sudo nano /etc/hosts
    
    ```
    
    找到類似：
    
    ```
    127.0.1.1    old-hostname
    
    ```
    
    改成：
    
    ```
    127.0.1.1    my-server
    
    ```
    
    > 這步驟避免本機解析錯誤。
3. 重新啟動或執行：
    
    ```bash
    sudo systemctl restart systemd-logind
    
    ```
    
    或乾脆 `sudo reboot`。

---

## 補充建議

- **雲端平台（AWS EC2、GCP、Azure）**：有些 VM 啟動時會被 Cloud-init 覆蓋，建議同時修改 `/etc/cloud/cloud.cfg` 將 `preserve_hostname` 設為 `true`。
- **影響範圍**：只影響系統層主機名稱，不會自動更新 SSL 憑證或 DNS 設定。

---

**總結**

> 建議使用 `sudo hostnamectl set-hostname <新名稱>`  
> 這是最簡單且官方推薦的方式，重登入即可生效。

# 【 Linux】Ubuntu 擴充硬碟完整步驟

#  Proxmox / Ubuntu 擴充硬碟完整步驟

### 💡 目標：

將原本的 `/dev/sda` 從 78G 擴充到 200G（或更大），  
並讓 `/`（根目錄）可使用整個新容量。

【proxmox】先使用ui + disk

[![image-1759893318494.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-10/scaled-1680-/8sUNIpNfeXM1xNvx-image-1759893318494.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-10/8sUNIpNfeXM1xNvx-image-1759893318494.png)

---

## 一、確認目前磁碟架構

```bash
lsblk

```

範例輸出：

```
sda
├─sda1  1M   BIOS boot
├─sda2  2G   /boot
└─sda3  78G  LVM → ubuntu--vg → ubuntu--lv → /

```

檢查磁碟實際大小：

```bash
sudo fdisk -l /dev/sda

```

確認 `sda3` 沒有覆蓋整顆磁碟（例如結束 sector 明顯在中段）。

---

## 二、調整分區（fdisk 擴大 /dev/sda3）

⚠️ **這步只修改分區表，不會刪資料。**  
關鍵是「Start sector 不變」，這樣 LVM 仍能識別。

```bash
sudo fdisk /dev/sda

```

依序輸入以下指令（每行結束按 Enter）：

```
Command (m for help): p
Command (m for help): d
Partition number (1-3, default 3): 3
Command (m for help): n
Partition number (3-128, default 3): 3
First sector (4198400-419430366, default 4198400): 4198400
Last sector, +/-sectors or +/-size{K,M,G,T,P} (4198400-419430366, default 419430366): [直接 Enter 使用預設值]

```

當出現：

```
Partition #3 contains a LVM2_member signature.
Do you want to remove the signature? [Y]es/[N]o:

```

👉 **輸入 `N`（No）保留資料！**

繼續：

```
Command (m for help): t
Partition number (1-3, default 3): 3
Hex code or alias (type L to list all): 8e
Command (m for help): w

```

---

## 三、重新讀取分區表

```bash
sudo partprobe

```

若出現 busy 警告，可忽略 `/dev/sr0`，只要 `/dev/sda3` 沒報錯即可。  
如有報錯，可重開機一次再繼續。

---

## 四、擴大 LVM 實體卷（PV）

```bash
sudo pvresize /dev/sda3

```

檢查是否成功：

```bash
sudo vgdisplay

```

你應該看到：

```
Free  PE / Size       >0 / 約120GiB

```

---

## 五、擴大邏輯卷（LV）

```bash
sudo lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv

```

範例輸出：

```
Size of logical volume ubuntu-vg/ubuntu-lv changed from <78.00 GiB to <198.00 GiB

```

---

## 六、擴大檔案系統（線上擴充）

先確認檔案系統類型：

```bash
df -Th /

```

結果可能是：

```
/dev/mapper/ubuntu--vg-ubuntu--lv ext4 ...

```

### ▸ 若是 ext4：

```bash
sudo resize2fs /dev/ubuntu-vg/ubuntu-lv

```

### ▸ 若是 xfs：

```bash
sudo xfs_growfs /

```

---

## 七、驗證結果

```bash
df -h

```

預期結果：

```
Filesystem                         Size  Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv  196G   20G  170G  11% /

```

---

## ✅ 狀態總結

<table id="bkmrk-%E9%A0%85%E7%9B%AE-%E7%B5%90%E6%9E%9C-%E5%88%86%E5%8D%80-%2Fdev%2Fsda3-%E5%B7%B2"><thead><tr><th>項目</th><th>結果</th></tr></thead><tbody><tr><td>分區 `/dev/sda3`</td><td>已擴大到整顆磁碟</td></tr><tr><td>PV (`pvresize`)</td><td>已更新容量</td></tr><tr><td>VG / LV (`lvextend`)</td><td>成功放大</td></tr><tr><td>檔案系統 (`resize2fs`)</td><td>線上擴充完成</td></tr><tr><td>是否需重開機</td><td>❌ 不需要</td></tr></tbody></table>

---

## 💡 小提醒

- 如果修改的是 `/boot` 或 EFI 分區，才需要重開機。
- 若 `partprobe` 無法重新讀取分區表，可在安全狀態下 reboot。
- 這個流程對 Ubuntu / Debian / Proxmox VM 都適用。

---

# 【Linux】swap相關

[![image-1766024226901.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-12/scaled-1680-/yTgfr35JUr1Zp9If-image-1766024226901.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-12/yTgfr35JUr1Zp9If-image-1766024226901.png)

從你貼的 `top` 看起來 **Swap 使用率偏高**（`KiB Swap: 79.8/1044476` 很像是 79.8% / 1,044,476 KiB），要「釋放 swap」本質上就是**把已經換到 swap 的頁面搬回 RAM**。最直接的方法是 **swapoff → swapon**（會短暫吃 RAM、可能卡頓一下）。

## ✅ 1) 先確認 RAM 夠不夠（很重要）

```bash
free -h
swapon --show

```

看 `available`（可用記憶體）要 **大於目前 swap 已用量**（最好再多留一些 buffer），不然 `swapoff` 可能 OOM 或把機器拖很慢。

```
[root@localhost ~]# free -h 
total used free shared buff/cache available 
Mem: 45G 25G 1.8G 226M 18G 20G 
Swap: 1.0G 814M 205M 

[root@localhost ~]# swapon --show 
NAME TYPE SIZE USED PRIO 
/dev/dm-1 partition 1020M 814.2M -2
```

## ✅ 2) 一次清空 swap（常用）

```bash
sudo swapoff -a
sudo swapon -a

```

清完再確認：

```bash
free -h
swapon --show

```

> 若你是 swapfile（例如 `/swap.img` 或 `/swapfile`），也可只針對單一檔案：

```bash
sudo swapoff /swap.img   # 或 /swapfile
sudo swapon  /swap.img

```

## ✅ 3) 找出「誰在吃 swap」（不然很快又會回來）

### 有 smem 最方便

```bash
sudo apt-get update && sudo apt-get install -y smem
sudo smem -rs swap | head

```

### 沒有 smem：用 /proc 快速抓（由大到小）

```bash
# who_use_swap.sh
#!/bin/bash
for p in /proc/[0-9]*; do
  pid=${p##*/}
  swap=$(awk '/VmSwap:/ {print $2}' $p/status 2>/dev/null)
  comm=$(tr -d '\0' < $p/comm 2>/dev/null)
  if [ -n "$swap" ] && [ "$swap" -gt 0 ]; then
    printf "%10s KiB  pid=%s  %s\n" "$swap" "$pid" "$comm"
  fi
done | sort -nr | head -20

```

⚠️ 小提醒：`swapoff` 期間會把資料搬回 RAM，可能造成短暫卡頓/IO 增加；如果是線上服務，建議離峰做，或先找出吃 swap 的程序再處理。

---

### ✅ swapoff 如何觀察？

用 `watch` 最方便：

```bash
watch -n 5 free -h

```

想加上時間戳一起看：

```bash
watch -n 5 'date; echo; free -h'

```

離開按 `q`（或 `Ctrl+C`）。

```
Every 5.0s: date; echo; free -h                                                                    
Thu Dec 18 10:23:47 2025

Thu Dec 18 10:23:47 CST 2025

              total        used        free	 shared  buff/cache   available
Mem:            45G         25G        994M        230M         18G         19G
Swap:          1.0G          0B        1.0G
```

---

### ✅ 什麼情況才需要「清快取」？

以下有需要放快取，獲得記憶體空間嗎

```
Every 5.0s: date; echo; free -h                                                                    
Thu Dec 18 10:23:47 2025

Thu Dec 18 10:23:47 CST 2025

              total        used        free	 shared  buff/cache   available
Mem:            45G         25G        994M        230M         18G         19G
Swap:          1.0G          0B        1.0G
```

你現在重點要看的是：

- `available = 19G`：代表系統評估「真的能拿來給程式用」的記憶體還有 19G（包含可回收的 cache）。這個數字大 → **不缺記憶體**。
- `buff/cache = 18G`：這是 Linux 用來加速 I/O 的檔案快取，**需要時會自動回收**，不是浪費。
- `free = 1.5G` 很低是正常現象，Linux 會盡量把閒置 RAM 拿去當 cache。

所以：**沒有必要清 cache**，清了反而會讓磁碟/檔案讀取變慢（cache 被你清掉了）。

---

### ✅ 如何判斷「到底有沒有影響」：

```bash
vmstat 1

```

看 `si` / `so`（swap in / swap out）：

- 長時間都是 0 → swap 雖然滿，但**幾乎沒在動**，影響很小
- 一直有數字跳 → 有在頻繁換頁，**延遲抖動**就會比較明顯，建議處理或找出吃 swap 的程序

```
[root@localhost ~]# vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0 354480 1360744    788 19839980    0    0     0    16    0    0  2  1 97  0  0
 1  0 354040 1360720    788 19840012  440    0   440   778 10902 12281  2  7 91  0  0
 2  0 353108 1357988    788 19840088  932    0   932    83 14391 17944  5  8 87  0  0
 2  0 352616 1355888    788 19840140  460    0   460   117 15671 19400  7  8 85  0  0
 3  0 351912 1357268    788 19840132  640    0   640   142 15713 19048  5  8 87  0  0
 2  0 351448 1355556    788 19840140  436    0   436    92 13794 15914  2  8 90  0  0
 2  0 350628 1357180    788 19840156  720    0   720   174 18513 21545  4  9 87  0  0
 1  0 349144 1355736    788 19840136 1484    0  1484    77 11470 13126  2  7 91  0  0
 2  0 348476 1354372    788 19840136  668    0   668    79 11530 12891  2  7 91  0  0
^C
```

你這段 `vmstat 1` **顯示「swap 正在被讀回 RAM」**，而且是連續的：

- `si`（swap in）一直有 **440 / 932 / 1484 … KB/s**
- `so`（swap out）是 0
- `swpd` 也在 **一路下降**（354480 → 348476 …）

這代表：**有程式正在碰到曾經被換到 swap 的記憶體頁面**，系統就把它從 swap 讀回來；目前沒有再把新的頁面換出去。

### 會有什麼影響？

1. **尾延遲抖動（最典型）**  
    只要 `si` 不為 0，就表示有 swap I/O；那段時間碰到這些頁面的 thread 可能會卡一下（但你 `wa=0` 表示目前 I/O 沒被堵住，所以感受可能不明顯或很輕微）。
2. **swap 滿會降低緩衝空間**  
    現在 `so=0` 代表還沒再把東西換出去；但如果之後遇到瞬間記憶體壓力，swap 已滿會讓系統更容易走向 OOM / 更劇烈 reclaim。

---

## ✅ swap 不清會有什麼影響

不清 swap 通常**不會有立即性壞處**，但會有幾個「可能的影響」，看你的工作型態而定：

### 1) 效能：偶爾的延遲抖動（最常見）

swap 裡的資料如果之後又被用到，就會發生 **page-in**（從磁碟/SSD 讀回 RAM），比 RAM 慢很多，所以可能出現：

- API / job 偶發性變慢、尾延遲（p95/p99）變差
- 某個服務「突然卡一下」

你現在 swap 只有 520MB，通常影響不大；但若剛好那 520MB 裡面是常被碰到的頁面，就會感覺得到。

### 2) 反映「過去曾經記憶體吃緊」

swap 被用滿常代表之前某個時段 RAM 壓力大（尖峰、記憶體洩漏、或短時間大量 cache / buffer / workload）。

- **不清 swap** 不代表現在還缺 RAM（你 `available` 很大就不缺）
- 但它是一個訊號：建議你偶爾追一下「誰在用 swap」避免問題重演

### 3) swap 滿了之後的風險：下次再吃緊會更容易出事

swap 如果已經 100%：

- 下次記憶體再緊，系統沒有 swap 空間可用，可能更快走向 **OOM kill**
- 或者進入更劇烈的 reclaim/抖動（視你的 kernel/設定）

### 4) 不清 swap 本身不會「佔著 RAM」

很多人誤會：swap 滿了會把 RAM 壓縮。其實 swap 佔的是磁碟區塊，不會直接吃掉你的 RAM。  
你真正該看的仍是 `available`、以及是否有頻繁 swap in/out。

---

### ✅ 説明 /dev/dm-1 意義 ， 與 -a 哪裡不同  
sudo swapoff /dev/dm-1   
sudo swapon /dev/dm-1

好，先用你現在看到的資訊來對照講：

## `/dev/dm-1` 是什麼意思？

- `dm` = **device-mapper**（Linux 的「裝置映射」層）
- `/dev/dm-1` 代表「第 2 個 device-mapper 裝置」（編號從 0 開始），通常是：
    
    
    - **LVM logical volume**（最常見，例如 `centos-swap`）
    - 或 dm-crypt / multipath 這類透過 device-mapper 建出來的裝置
- 因為你 `swapon --show` 顯示它是 `TYPE partition`，表示它是一個「區塊裝置/分割區型態」拿來當 swap。

想看它真正對應到什麼名字（建議你查一下）：

```bash
ls -l /dev/dm-1
lsblk -f
sudo dmsetup info -C
sudo dmsetup ls --tree

```

通常你會看到像 `centos-swap` 或 `vgname-swap` 之類的 LV 名稱。

---

## `-a` 跟指定 `/dev/dm-1` 有什麼不同？

### 1) 作用範圍不同

- `swapoff /dev/dm-1`：**只針對這一個 swap**
- `swapoff -a`：**針對所有已啟用的 swap**（全部停用）
- `swapon -a`：把 `/etc/fstab` 裡標記為 swap 的項目 **全部啟用回來**

### 2) 安全性/可控性不同

- 如果你機器上只有一個 swap（你現在看起來是），兩者結果幾乎一樣。
- 如果你有多個 swap（例如：一個 swap 分割區 + 一個 swapfile），用 `-a` 會一次動到全部；指定裝置比較可控，避免不小心把你不想動的 swap 也關掉。

### 3) `-a` 依賴 `/etc/fstab`

- `swapon -a` 會依照 `/etc/fstab` 去開啟 swap
- 若某個 swap 沒寫在 fstab、或用系統/雲平台動態掛的，`-a` 可能不會把它開回來；指定 `/dev/dm-1` 就不受這個影響。

---

## 你這台怎麼選？

你現在 `swapon --show` 只有一個 `/dev/dm-1`，所以：

- 想精準可控：用你貼的這兩行 ✅
- 想一鍵通用：`swapoff -a; swapon -a` 也可以 ✅

---

## ✅ 降低未來再大量使用 swap（可選）

```bash
sudo sysctl -w vm.swappiness=10

```

要永久生效：

```bash
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl --system

```

`sudo sysctl -w vm.swappiness=10` 這行是在 **「立即」調整 Linux kernel 對 swap 的使用傾向**。

---

- `sysctl`：用來讀/寫 Linux kernel 的即時參數（位於 `/proc/sys/`）
- `-w`：write（寫入）
- `vm.swappiness`：控制 **「多願意把記憶體頁面換到 swap」** 的參數
- `=10`：把 swappiness 設成 10（偏向少用 swap）

等價於：

```bash
echo 10 | sudo tee /proc/sys/vm/swappiness

```

---

#### vm.swappiness 代表什麼？

#### 範圍（常見）

- 通常是 **0 ~ 100**
- 數字越大 → kernel 越積極把「較少用到的記憶體頁面」換到 swap（更早換、更常換）
- 數字越小 → kernel 越傾向把資料留在 RAM、寧可回收 page cache，也比較不主動換到 swap

> 很多人會用 10 或 1 來降低 swap 對延遲的影響（尤其是 DB、API、線上服務）。

---

##### 調成 10 的「實際效果」

- **更晚才開始 swap out**（把 RAM 的匿名頁面換到 swap）
- 會更偏向：
    
    
    - 先回收 / 讓出 filesystem cache（`buff/cache`）
    - 盡量把程式的工作集留在 RAM
- 通常可降低：
    
    
    - 尾延遲抖動（p95/p99）
    - swap I/O 造成的卡頓

---

##### 常見誤解：swappiness=10 會把現有 swap 清掉嗎？

不會。

- `swappiness` 只影響「未來是否要換出去」的策略
- 你現在已經在 swap 裡的頁面，不會因為改成 10 就自動搬回 RAM  
    要清掉還是要靠：

```bash
swapoff -a && swapon -a

```

---

#### 風險 / 代價是什麼？

- 如果 swappiness 設太低（例如 0 或 1），在記憶體壓力來時：
    
    
    - kernel 會更晚使用 swap
    - 可能更快觸發 **OOM kill**（把某些程序殺掉）來保護系統
- 所以對「希望系統穩住不要 OOM」的場景，有時會保留比較高的值（例如 20~60），讓 swap 當緩衝。

> 你的機器 RAM 45G、swap 很小（520M~1G），把 swappiness 降到 10 通常是合理的偏好：少 swap、降低抖動。

---

##### 這設定會持久嗎？

不會。重開機會還原。

要永久化：

```bash
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl --system

```

---

##### 建議你怎麼驗證有沒有幫助？

改完後觀察：

```bash
vmstat 1

```

看 `so`（swap out）是否長期維持 0，系統是否少出現延遲抖動。

如果你願意貼 `cat /proc/sys/vm/swappiness` 以及你的服務類型（DB / API / ELK / Docker host），我可以給你更適合的 swappiness 建議值。