【Treeman】Linux
- shell
- 【jq】 linux json 查詢工具(查詢篇)
- 【jq】 linux json 查詢工具(修改篇)
- 【jq】相關連結
- 【Shell】【tip】 找出目前誰使用swap
- 【Shell】【tip】使用暫存表,更新crontab
- 【Shell】【tip】使用 echo 輸出排程時間到log
- 【Shell】linux 下source、sh、bash、./執行指令碼的區別
- 【Shell】date 做 timestamp 格式轉換計算
- 【Shell】awk 取出特定欄位
- 【Shell】取得pubic ip
- 【shell】內建變數
- 【Shell】動態呼叫awk自訂函數
- 【 Shell】【Tool】測試機器防火牆是否開通
- 【Shell】【Tool】批次更新密碼
- 【Shell】【Tip】列出環境變數網址解析dns
- 【Shell】【Tool】call_api
- 【茶包射手】
- 【網路相關】
- 【Top】 效能監控工具
- 【資安】強制登出使用者
- 【圖形介面】Linux安裝圖形介面
- 【圖形介面】tiger vnc
- 【Linux】帳號管理
- 【Linux】記憶體相關
- 【crontab】排程相關
- 【vim】快速鍵
- 【Linux】時區設定
- 【Linux】載入順序
- 【更新】Centos 7已於2024年6月30日停止維護,更換yum源解決yum404問題
- 【Linux】DNS 相關
- 【Linux】Dns 動態解析
- 【Linux】【權限管理】【acl】設定/var/www/html for tomcat 可讀寫
- 【Linux】SSH 跳板機設定
- 【Tool】tmux
- 【Linux】【Ubuntu】 中更改 hostname
- 【 Linux】Ubuntu 擴充硬碟完整步驟
- 【Linux】swap相關
shell
【jq】 linux json 查詢工具(查詢篇)
- data
{
"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)
[root@jylee-stres-stest250 tmp]# cat data.json | jq .age
25
- 取得資料(city) (addres > city)
[root@jylee-stres-stest250 tmp]# cat data.json | jq .address.city
"New York"
- 輸出不帶雙引號 -r :
[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 > city)
[root@jylee-stres-stest250 tmp]# cat data.json | jq .address.city
"New York"
- 取得電話
# 取得電話陣列
[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重組資料
[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 過濾資料
[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取出值
# 回傳物件
[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
}
]
# 綜合查詢
# @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
{
"values": [
{
"email": "user1@domain.com",
"id": "USER1_ID"
},{
"email": "user2@domain.com",
"id": "USER2_ID"
}
]
}
ss.sh
while IFS=',' read -r email id; do
echo "$email"
echo "$id"
done <<EOF
$(jq -r '.values[] | "\(.email),\(.id)"' data2.json)
EOF
[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
【jq】 linux json 查詢工具(修改篇)
- 修改前節點資料
[root@jylee-stres-stest250 tmp]# echo '{"a":{"b":0, "c":1}}' | jq .
{
"a": {
"b": 0,
"c": 1
}
}
- 新增 / 修改 : setpath([
路徑];值)
[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】相關連結
【Shell】【tip】 找出目前誰使用swap
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)
[ -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
#!/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
# 使用 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)
bash shell的命令分為兩類:外部命令和內部命令。外部命令是通過系統呼叫或獨立的程式實現的,如sed、awk等等。內部命令是由特殊的檔案格式(.def)所實現,如cd、history、exec等等。
在說明exe和source的區別之前,先說明一下fork的概念。
fork是linux的系統呼叫,用來建立子程序(child process)。子程序是父程序(parent process)的一個副本,從父程序那裡獲得一定的資源分配以及繼承父程序的環境。子程序與父程序唯一不同的地方在於pid(process id)。
環境變數(傳給子程序的變數,遺傳性是本地變數和環境變數的根本區別)只能單向從父程序傳給子程序。不管子程序的環境變數如何變化,都不會影響父程序的環境變數。
shell script:
新產生一個shell然後再執行scripts的方法是在scripts檔案開頭加入以下語句
#!/bin/sh
一般的script檔案(.sh)即是這種用法。這種方法先啟用新的sub-shell(新的子程序),然後在其下執行命令。
另外一種方法就是上面說過的source命令,不再產生新的shell,而在當前shell下執行一切命令。
source:
source命令即點(.)命令。
在bash下輸入man source,找到source命令解釋處,可以看到解釋"Read and execute commands from filename in the current shell environment and ..."。從中可以知道,source命令是在當前程序中執行引數檔案中的各個命令,而不是另起子程序(或sub-shell)。
exec:
在bash下輸入man exec,找到exec命令解釋處,可以看到有"No new process is created."這樣的解釋,這就是說exec命令不產生新的子程序。那麼exec與source的區別是什麼呢?
* 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
#!/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
#!/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"
./1.sh fork
可以看到,1.sh是在父程序中執行,2.sh是在子程序中執行的,父程序的PID是5344,而子程序的是5345,當子程序執行完畢後,控制權返回到父程序。同時,在子程序改變環境變數A的值不會影響到父程序。
./1.sh source
由結果可知,1.sh和2.sh都是在同一程序中執行的,PID為5367
./1.sh exec
可知,兩個指令碼都是在同一程序中執行,但是請注意,使用exec終止了原來的父程序,因此,可以看到
echo "PID for 1.sh after exec/source/fork:$"
echo "1.sh: \$A is $A"
這兩個命令沒有執行
由這個例子,便大致可瞭解它們的區別了
【Shell】date 做 timestamp 格式轉換計算
要將日期與 timestamp 互相做格式轉換,只要使用 date 就可以達成,date 還可以順便做些日期的加減換算。
- 日期轉換成 timestamp
- $ date -d "2018-02-08" +%s
1518019200
- $ date -d "2018-02-08" +%s
- 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 --date=@1518019200
- 日期加減換算
- $ date -d "-5 day" "+%Y-%m-%d" # 5天前的日期
<來源>Linux 使用 date 做 timestamp 格式轉換計算 – Tsung's Blog (longwin.com.tw)
【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)
- AWK 取得欄位的「最後」與「倒數第二個值」的方法 – Tsung's Blog (longwin.com.tw)
【Shell】取得pubic ip
#curl ifconfig.me; echo
175.99.111.111
【shell】內建變數
| $BASH_ENV | absolute path of startup file |
| $CDPATH | directories searched by cd |
| $FCEDIT | absolute path of history editor |
| $HISTCMD | the history number of the current command |
| $HISFILE | absolute path of history file |
| $HISTSIZE | number of remembered commands |
| $HOME | login directory |
| $IFS | token delimiters |
| $LINENO | current line number in shell script |
| $LINES | terminal height |
| absolute path of mailbox | |
| $MAILCHECK | number of seconds to check mail |
| $OLDPWD | absolute path of previous directory |
| $OPTARG | option set by getopt |
| $OPTIND | option's ordinal position set by getopt |
| $OSTYPE | the OS on which bash is executing |
| $PATH | command search path |
| $PPID | process ID of parent |
| $PS1 | primary prompt |
| $PS2 | secondary prompt |
| $PWD | absolute path of current directory |
| $RANDOM | random integer |
| $REPLY | default variable for read |
| $SECONDS | number of seconds since shell started |
| $SHELL | absolute pathname of preferred shell |
| $TMOUT | seconds to log out after lack of use |
| $UID | user ID of the current user |
| $$ | process ID of current shell |
| $? | exit status of most recent statement |
| $# | 參數的數目 |
| $* | 代表所有參數 |
| $! | PID of the most recently started backgroup job |
【Shell】動態呼叫awk自訂函數
*test.sh
#!/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
[root]$ sh test.sh 6666
6.5M
[root]$ sh test.sh 66666
65.1M
[root]$ sh test.sh 666666666
635.78G
【 Shell】【Tool】測試機器防火牆是否開通
#!/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,可以按照以下步驟操作:
-
建立使用者清單:
在
users.txt中列出所有需要重設密碼的使用者名稱,每行一個。例如:user1 user2 user3 -
編寫批次重設密碼的腳本:
建立一個 Shell 腳本,批次執行密碼重設並強制更新。例如,將以下內容儲存為
reset_password.sh:#!/bin/bash # 檢查 users.txt 是否存在 if [[ ! -f users.txt ]]; then echo "Error: users.txt not found!" exit 1 fi # 循環讀取每一個使用者 while IFS= read -r username; do # 跳過空行 if [[ -z "$username" ]]; then continue fi # 設定預設密碼或隨機密碼 new_password="DefaultPassword123" # 或使用 `new_password=$(openssl rand -base64 12)` 生成隨機密碼 echo "重設 $username 的密碼為 $new_password" # 重設密碼 echo "$username:$new_password" | sudo chpasswd # 強制下次登入時更改密碼 sudo chage -d 0 "$username" echo "$username 的密碼已重設並強制更新" done < users.txt -
執行腳本:
為腳本增加執行權限,然後執行:
chmod +x reset_password.sh sudo ./reset_password.sh -
檢查結果:
執行完成後,你可以再次使用
chage -l username指令逐一檢查使用者的密碼到期設定,確認是否成功。
這樣便可批次重設多位使用者的密碼,並強制其在下次登入時更新密碼。
【Shell】【Tip】列出環境變數網址解析dns
#!/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
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 參數的作用,並且說明為什麼要加:
call_api() {
curl -fsS \
--connect-timeout 2 \
--max-time 5 \
--retry 2 --retry-delay 1 \
-w " [HTTP_CODE=%{http_code}]\n" \
"$URL"
}
參數逐一解析
-
-f/--fail-
作用:當 HTTP 狀態碼是 4xx / 5xx 時,不會把 HTML 錯誤頁輸出到 stdout,而是直接返回非零 exit code。
-
好處:可以在 script 判斷「這次呼叫失敗」,而不是誤以為拿到一大串錯誤 HTML 也算成功。
-
-
-s/--silent-
作用:靜默模式,不顯示下載進度條與錯誤訊息(錯誤還是會 exit code ≠ 0)。
-
好處:避免 cron job 或 log 裡塞滿進度百分比。
-
-
-S/--show-error-
作用:在
-s靜默模式下,仍然把錯誤訊息(例如 DNS 解析失敗、timeout)顯示出來。 -
好處:結合
-s使用,可以安靜但又不失錯誤訊息。
-
-
--connect-timeout 2-
作用:設定 TCP 連線的最大等待時間(單位:秒)。
-
例子:如果 API 服務根本沒在聽 port 8082,curl 會在 2 秒 內放棄。
-
-
--max-time 5-
作用:整個 request(連線、傳輸、讀取)的最長時間,超過就中斷。
-
好處:避免 API 掛住時,curl 一直卡在那裡。
-
-
--retry 2-
作用:遇到「暫時性錯誤」(像 DNS 解析失敗、timeout、connection reset by peer),會自動重試最多 2 次。
-
預設只會 retry network-level 的錯誤,不會 retry 4xx/5xx。
-
-
--retry-delay 1-
作用:每次 retry 前延遲 1 秒。
-
預設會 delay 1 秒,但這裡寫死確保一致。
-
-
-w " [HTTP_CODE=%{http_code}]\n"-
作用:在 response 輸出結尾,附加 HTTP 狀態碼。
-
例子:成功時會多一行
[HTTP_CODE=200] -
好處:log 裡可以直接看出 API 狀態。
-
-
"$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
【cron】沒有依排程執行
檢查服務
# 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
# 檢查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
ls /etc/netplan/
若此資料夾存在 YAML 檔案(例如 00-installer-config.yaml),代表使用 Netplan。
2️⃣ Netplan 修改(Ubuntu 18.04+)
-
查詢目前網卡名稱
ip link例如
ens33、enp0s3。 -
編輯設定檔
sudo nano /etc/netplan/00-installer-config.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] -
套用設定
sudo netplan apply建議第一次可用
sudo netplan try,若有誤會自動還原。
3️⃣ 傳統 /etc/network/interfaces(Ubuntu 16.04 或自行安裝 ifupdown)
-
編輯:
sudo nano /etc/network/interfaces -
範例:
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 -
套用:
sudo systemctl restart networking
4️⃣ 臨時修改 IP(重開機後失效)
若只想暫時修改:
sudo ip addr add 192.168.1.100/24 dev ens33
sudo ip route add default via 192.168.1.1
5️⃣ 驗證設定
ip addr show
ip route
ping 8.8.8.8
小提醒
-
若同時使用 VPN 或 Cloud Provider(AWS/GCP/Azure),請確認平台的網卡策略與安全群組。
-
修改前最好備份設定檔:
sudo cp /etc/netplan/00-installer-config.yaml /etc/netplan/00-installer-config.yaml.bak
以上即可依版本與需求選擇永久或臨時的 Ubuntu IP 修改方式。
如果出現警告畫面
從這張截圖可以看到兩類主要警告訊息:
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,例如:
sudo chmod 600 /etc/netplan/00-installer-config.yaml
# 或若需要同群組讀取
sudo chmod 640 /etc/netplan/00-installer-config.yaml
再確認:
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。
修改範例
舊:
ethernets:
ens33:
dhcp4: no
addresses: [192.168.1.100/24]
gateway4: 192.168.1.1
新:
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 可以忽略;若有需要,就啟動服務:
sudo systemctl enable --now ovsdb-server
建議修正步驟總結
-
調整權限
sudo chmod 600 /etc/netplan/00-installer-config.yaml -
修改 YAML 把
gateway4換成 routes -
驗證語法:
sudo netplan try沒問題後:
sudo netplan apply
這樣就能消除「Permissions too open」與「gateway4 deprecated」兩個警告。
【Top】 效能監控工具
top - 16:12:13 up 42 days, 37 min, 6 users, load average: 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)進行排序。你可以按下鍵盤上的`<`或`>`鍵來改變排序方式。例如,按下`<`鍵可以按照進程ID(PID)進行排序,按下`>`鍵可以按照記憶體使用百分比(%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、&、<、>:定位操作:'L'/'&' 查找/重複查找;移動排序列:'<'/'>' 左移/右移。
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
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使用率排序:**
- 按下 `Shift + P`
2. **按内存使用率排序:**
- 按下 `Shift + M`
3. **按累计CPU使用率排序:**
- 按下 `t` 键,然后选择 `%CPU`
4. **按累计内存使用率排序:**
- 按下 `t` 键,然后选择 `%MEM`
5. **按PID(进程ID)排序:**
- 按下 `Shift + N`
6. **按进程名称排序:**
- 按下 `c` 键
7. **按照其他列排序:**
- 在`top`的显示界面中,你可以使用左右箭头键来选择不同的列,然后按下 `o` 键来选择要排序的列。
8. **切换升序/降序排序:**
- 在排序之后,你可以按下 `R` 键来切换排序的顺序(升序或降序)。
请注意,这些键仅在`top`运行时有效,按下相应的键后,`top`将根据你的选择重新排序并更新显示。如果你希望在`top`启动时就按照特定的排序方式显示,你可以使用`top`命令的一些参数,例如:
top -o %CPU # 按照 CPU 使用率降序排列
top -o %MEM # 按照内存使用率降序排列
这些是一些基本的排序操作,根据你的需求可以选择适合的排序方式。
🖥 CPU 使用率指標速查表
| 指標 | 全名 | 意義 | 判斷標準 |
|---|---|---|---|
| us | user | 使用者態 CPU(應用程式運算時間) | - 高代表程式在跑計算(正常)- 長期 80–90% 可能 CPU 算力不足 |
| sy | system | 核心態 CPU(系統呼叫/驅動/中斷) | - 偶爾 5–15% 正常- 長期 >30% 可能系統呼叫太頻繁或 driver 問題 |
| id | idle | CPU 閒置時間 | - 高表示 CPU 很空閒- 低(接近 0)表示 CPU 幾乎滿載 |
| wa | iowait | CPU 在等 I/O(磁碟/網路) | - 0–5% 正常- >20% 有 I/O 壓力- >50% 嚴重瓶頸,CPU 幾乎都在等磁碟/網路- 100% = 完全被 I/O 卡住 |
📊 看 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 瓶頸
📌 判斷邏輯
-
us 高 → 程式運算密集(算力不足)
-
sy 高 → 核心忙,可能 driver、系統呼叫過多
-
wa 高 → I/O 磁碟/網路瓶頸
-
id 低 → CPU 幾乎被榨乾
✅ 快速結論
-
CPU 不夠用 → us 高、id 低、wa 低
-
系統呼叫過多 → sy 高
-
磁碟/網路卡住 → wa 高
-
CPU 很空閒 → id 高
要不要我幫你再做一張 「CPU 狀態 → 問題來源 → 解決方法」的對照表,讓你排查更直觀?
【資安】強制登出使用者
使用who查詢在線使用者
pkill -kill -t {tty} 強制登出
【圖形介面】Linux安裝圖形介面
* 安裝圖形介面
```
yum groupinstall -y "GNOME Desktop"
```
|run level|<|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
複製設定檔
cp /lib/systemd/system/vncserver@.service /etc/systemd/system/vncserver@:1.service
以下是用root 帳號,如果是一般使用者,可使用註解區塊(使用者jcky,把root註解)
[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 密碼
vncpasswd
使用root 帳號
su -
systemctl daemon-reload
啟用服務,並設定開機自動執行
systemctl start vncserver@:1.service
systemctl enable vncserver@:1.service
可使用vnc
【Linux】帳號管理
新增/刪除帳號
# -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
設定密碼過期
#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 登入
# 修改/etc/ssh/sshd_config
vim /etc/ssh/sshd_config
# PermitRootLogin yes
# 改為
PermitRootLogin no
# 重新啟動 ssh服務
systemctl restart sshd
群駔相關
# 新增群組
# 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
釋放記憶體
echo 1 > /proc/sys/vm/drop_cache
釋放dentries、inodes所用的 cache memory
echo 2 > /proc/sys/vm/drop_caches
釋放pagecache、dentry、inode 所用的 cache memory
echo 3 > /proc/sys/vm/drop_caches
完全釋放cache memory,必須先執行sync,避免錯誤。
sync
#在釋放記憶體後再將/proc/sys/vm/drop_caches的值設為0
echo 0 > /proc/sys/vm/drop_caches
釋放swap
#此swap 在/dev/cobd1
swapoff /dev/cobd1;swapon /dev/cobd1
常用記憶體指令
free
vmstat
top
watch cat /proc/meminfo
【crontab】排程相關
使用nginx 設定排程
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多行註解
多行注释:
-
进入命令行模式,按ctrl + v进入 visual block模式(可视快模式),然后按j, 或者k选中多行,把需要注释的行标记起来
-
按大写字母I,再插入注释符,例如//
-
按esc键就会全部注释了(我的是按两下)
取消多行注释:
-
进入命令行模式,按ctrl + v进入 visual block模式(可视快模式),按小写字母l横向选中列的个数,例如 // 需要选中2列
-
按字母j,或者k选中注释符号
-
按d键就可全部取消注释
【Linux】時區設定
Centos7
timedatectl set-timezone Asia/Taipei
【Linux】載入順序
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 模式。例如以下命令:
ssh user@host <COMMAND> -
這張圖第二層的
--login ?的意思就算你的 Bash 不是執行在 Login shell 模式,你一樣可以在呼叫
/bin/bash的時候特別加上--login參數,這樣也可以被視為是 Login shell 模式。例如以下這段範例,就會被視為使用 Login shell 模式啟動:
#!/bin/bash --login -
這張圖第三層的
$BASH_ENV是什麼環境變數?當你將 Bash 啟動在非互動模式,也沒有特別加上
--login參數的情況,同時這也是大多數執行的預設值,Bash 會優先尋找目前的環境變數中有沒有一個名為$BASH_ENV的變數,這個變數其實是指向一個檔案路徑。你從man bash可以發現,Bash 在非互動模式啟動的時候,預設會執行以下命令:if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi這也意味者,他會去 sourcing
"$BASH_ENV"這個檔案! -
這張圖第三層的
--noprofile是什麼參數?執行
/bin/bash的時候,可以額外加上--noprofile參數,加上之後就不會載入任何啟動檔。如果你沒有加上
--noprofile參數,也是一般大多數命令的執行方式,預設是會先載入系統全域的/etc/profile檔案。注意:系統全域的
/etc/profile檔案,還會額外載入/etc/profile.d/*.sh檔案。然後再從
$HOME目錄下依序找到這三個檔案執行,但重點是,他只會選擇一個檔案來執行,先找到的先執行,後面的就不會執行!~/.bash_profile~/.bash_login~/.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 會依據以下順序載入:
-
~/.zshenv任何啟動情境下,都會載入這個檔案,請將各種環境變數請全部設定在這裡。
-
/etc/zsh/zprofile與~/.zprofile如果執行在 Login shell 才會依序執行
/etc/zsh/zprofile與~/.zprofile檔案。 -
/etc/zsh/zshrc與~/.zshrc如果執行在 Interactive 互動模式下,才會依序執行
/etc/zsh/zshrc與~/.zshrc檔案。 -
/etc/zsh/zlogin與~/.zlogin如果執行在 Login shell 下,最後才會依序執行
/etc/zsh/zlogin與~/.zlogin檔案。 -
~/.zlogout與/etc/zsh/zlogout當你用
exit或logout命令登出時,會自動依序執行~/.zlogout與/etc/zsh/zlogout檔案。
我只能說,Zsh 的啟動順序實在比 Bash 好理解太多了,也沒什麼地雷! 👍
總結幾種常見情境
-
直接呼叫某個 Shell Script
#!/bin/bash [ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive" shopt -q login_shell && echo "Login shell" || echo "Not login shell"Interactive?👉NoLogin shell?👉No--noprofile?👉No
執行時完全不會載入任何新的啟動檔設定!
-
使用 SSH 登入遠端主機
ssh user@host依序載入
/etc/profile-->/etc/bash.bashrc-->~/.profile[ ->~/.bashrc]Interactive?👉YesLogin shell?👉Yes--noprofile?👉No
注意: Bash 不會主動載入
~/.bashrc執行,而是我們通常在~/.profile會載入~/.bashrc執行。 -
使用 SSH 登入遠端主機後,執行
bash命令ssh user@host剛登入,依序載入
/etc/profile-->/etc/bash.bashrc-->~/.profile[ ->~/.bashrc]Interactive?👉YesLogin shell?👉Yes--noprofile?👉No
然後我們在遠端主機執行
bash命令,進入下一層 Shell 環境:bash登入後執行
bash預設是互動模式,會依序載入/etc/bash.bashrc-->~/.bashrcInteractive?👉YesLogin shell?👉No--rcfile <file> ?👉No--norc ?👉No
-
使用 SSH 登入遠端主機後,執行
bash -c '<command>'命令ssh user@host剛登入,依序載入
/etc/profile-->/etc/bash.bashrc-->~/.profile[ ->~/.bashrc]Interactive?👉YesLogin shell?👉Yes--noprofile?👉No
bash -c '[ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"'登入後執行
bash -c命令屬於非互動模式,而且沒有$BASH_ENV的情況下,不會載入任何啟動檔!Interactive?👉NoLogin shell?👉No$BASH_ENV👉No
-
使用 SSH 執行遠端命令
ssh user@host '[ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"' ssh user@host 'shopt -q login_shell && echo "Login shell" || echo "Not login shell"'透過 SSH 執行遠端命令,預設將會跑在 Non-Interactive 模式下,但還是會依序載入
/etc/bash.bashrc-->~/.bashrcInteractive?👉NoLogin shell?👉No
如果你想在執行遠端命令時載入額外的啟動檔,也可以這樣寫:
ssh user@host "source /etc/profile; source ~/.profile; /your/script.sh" -
使用 SSH 執行遠端命令,透過
bash呼叫另一個命令ssh user@host -t 'bash -c '"'"'[ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"'"'"'' ssh user@host -t 'bash -c '"'"'shopt -q login_shell && echo "Login shell" || echo "Not login shell"'"'"''要在單引號(
')的字串中間加入一個單引號,必須輸入五個字元'"'"'才能代表一個單引號!(噁心的語法)在 ssh 執行時加入
-t參數,可以在遠端執行時取得一個 pseudo-terminal allocation (pts/0)!透過 SSH 執行遠端命令,預設將會跑在 Non-Interactive 模式下,但還是會依序載入
/etc/bash.bashrc-->~/.bashrcInteractive?👉NoLogin shell?👉No
第二層
bash應該是跑在 Non-Interactive 模式,但是卻依序載入/etc/bash.bashrc-->~/.bashrc檔案,這部分我還沒辦法理解為什麼會這樣,感覺透過 SSH 執行遠端程式,預設就會載入這兩個檔案!Interactive?👉NoLogin shell?👉No
底下這段是讓命令跑在 Interactive 模式,但載入啟動檔的順序竟然跟 Non-Interactive 竟然一樣!
ssh user@host -t 'bash -i -c '"'"'[ -z "$PS1" ] && echo "Non-Interactive" || echo "Interactive"'"'"'' ssh user@host -t 'bash -i -c '"'"'shopt -q login_shell && echo "Login shell" || echo "Not login shell"'"'"''簡單來說,使用 SSH 直接遠端命令時,使用
-i(互動模式) 或不使用-i對載入啟動檔沒有什麼兩樣,不過都會載入兩次。不過還是有一個地方不同,如果你的啟動檔有設定 alias 的話,只有使用-i互動模式才能使用。例如以下這段命令,就會得到
bash: ll: command not found的錯誤:ssh user@host -t 'll'ssh user@host -t bash -c 'll'如果改用
-i來執行,就可以正常執行ll這個 alias 命令:ssh user@host -t bash -i -c 'll' -
使用 SSH 執行遠端命令,並以 Login shell 啟動 Bash
ssh user@host bash -l -c 'set'第一層
bash依序載入/etc/bash.bashrc-->~/.bashrc第二層
bash依序載入/etc/profile-->~/.profile[ ->~/.bashrc]
【更新】Centos 7已於2024年6月30日停止維護,更換yum源解決yum404問題
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 設定在哪
# 查詢/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 記錄。
配置步驟
-
安裝 Bind 安裝 Bind DNS Server:
sudo apt install bind9 -
配置視圖(View) 編輯 Bind 的主配置文件
/etc/bind/named.conf,添加基於來源 IP 的視圖。範例配置:
acl "internal-network" { 192.168.1.0/24; }; acl "external-network" { any; }; view "internal" { match-clients { "internal-network"; }; zone "example.com" { type master; file "/etc/bind/db.internal.example.com"; }; }; view "external" { match-clients { "external-network"; }; zone "example.com" { type master; file "/etc/bind/db.external.example.com"; }; }; -
創建區域文件 根據來源 IP 創建不同的區域文件。例如:
-
內部網絡的區域文件
/etc/bind/db.internal.example.com:$TTL 604800 @ IN SOA example.com. admin.example.com. ( 1 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL ; @ IN NS ns.example.com. @ IN A 192.168.1.1 -
外部網絡的區域文件
/etc/bind/db.external.example.com:$TTL 604800 @ IN SOA example.com. admin.example.com. ( 1 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL ; @ IN NS ns.example.com. @ IN A 203.0.113.1
-
-
重啟 Bind 重啟 Bind 服務:
sudo systemctl restart bind9
使用 dnsmasq 實現(輕量級)
配置步驟
-
安裝 dnsmasq
sudo apt install dnsmasq -
配置條件解析 編輯
/etc/dnsmasq.conf,添加基於來源 IP 的條件解析規則:# 為內部網絡配置 dhcp-range=set:internal,192.168.1.0,192.168.1.255 address=/example.com/192.168.1.1 # 為外部網絡配置 dhcp-range=set:external,0.0.0.0,255.255.255.255 address=/example.com/203.0.113.1 -
重啟 dnsmasq
sudo systemctl restart dnsmasq
使用 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://doc.powerdns.com/authoritative/indexTOC.html
- https://blog.csdn.net/sdhzdtwhm/article/details/135910057
- https://lantian.pub/article/modify-website/powerdns-lua-diy-geodns.lantian/
- https://github.com/PowerDNS/pdns/blob/master/docker-compose.yml
配置 PowerDNS + Lua 脚本
PowerDNS 支持使用 Lua 腳本進行動態解析。
-
安裝 PowerDNS 和相關組件
sudo apt install pdns-server pdns-backend-sqlite3 -
啟用 Lua 腳本支持 在
/etc/powerdns/pdns.conf中啟用 Lua Backend:launch=gsqlite3,lua lua-dns-script=/etc/powerdns/dns.lua -
編寫 Lua 腳本 創建
/etc/powerdns/dns.lua,根據來源 IP 返回不同的結果:function postresolve(remoteip, domain, qtype) if string.match(remoteip, "^192%.168%.1%.") then return {{qtype="A", content="192.168.1.1", ttl=60}} else return {{qtype="A", content="203.0.113.1", ttl=60}} end end -
重啟 PowerDNS
sudo systemctl restart pdns
測試
使用 dig 測試
針對不同的來源 IP 測試返回的解析結果。
-
從內部網絡:
dig @<DNS_SERVER_IP> example.com返回內部地址(例如
192.168.1.1)。 -
從外部網絡:
dig @<DNS_SERVER_IP> example.com返回外部地址(例如
203.0.113.1)。
注意事項
-
來源 IP 檢測:
- 檢查網絡路由,確保 DNS 伺服器能正確檢測請求的來源 IP。
- 若有 NAT 設置,需配置正確的來源地址。
-
DNS 緩存:
- 若存在 DNS 緩存伺服器,可能會干擾條件解析結果。
-
性能:
Bind和PowerDNS適合高流量環境。dnsmasq適合輕量級應用場景。
這些方案可以靈活地根據來源 IP 實現不同的 DNS 解析結果。
【Linux】【權限管理】【acl】設定/var/www/html for tomcat 可讀寫
以下是如何在 Ubuntu 中設置這些 ACL 權限的步驟。
步驟 1:啟用 ACL 支持
如果 ACL 尚未啟用,請確保相關功能已經安裝並啟用:
- 安裝 ACL 工具:
sudo apt install acl - 確保文件系統支持 ACL:
sudo mount -o remount,acl /
步驟 2:設置基本權限
設定 /var/www/html 的基本權限:
sudo chmod 775 /var/www/html
sudo chown root:root /var/www/html
步驟 3:配置 ACL 權限
根據需求逐步配置 ACL:
-
設置具體使用者和群組的 ACL 權限:
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/htmlu:tomcat:rwx:給tomcat用戶設置rwx權限。g::r-x:默認組的權限為r-x。o::r-x:其他人的權限為r-x。
-
設置默認 ACL(適用於未來子目錄和文件):
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(對新創建的文件/目錄生效)。
步驟 4:檢查 ACL 設置
使用以下指令檢查 ACL 配置是否正確:
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 設置的解釋
-
普通權限:
user::rwx:目錄擁有者root的權限。user:tomcat:rwx:為用戶tomcat設置的權限。group::r-x:目錄的群組成員的權限。other::r-x:其他用戶的權限。mask::rwx:ACL 中權限的上限(最高可設置為rwx)。
-
默認權限(
default:開頭):default:user::rwx:默認擁有者的權限。default:user:tomcat:rwx:默認為tomcat設置的權限。default:group::r-x:默認群組的權限。default:other::r-x:默認其他用戶的權限。
總結指令
以下是一鍵完成配置的指令集合:
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
-
需要設定
-d的情況- 如果您希望 新創建的文件或子目錄 自動繼承特定的 ACL 設置(如
tomcat擁有特定權限),那麼您需要設置默認 ACL。例如:/var/www/html是一個共享的目錄,將會持續新增文件。- 您希望
tomcat用戶對新增的文件有rwx權限。
sudo setfacl -d -m u:tomcat:rwx /var/www/html當某人創建一個新文件時,
tomcat就會自動擁有該文件的權限。 - 如果您希望 新創建的文件或子目錄 自動繼承特定的 ACL 設置(如
-
不需要設定
-d的情況- 如果您僅關心目錄 當前的文件和目錄,而不需要未來的新增文件繼承權限。
- 如果您會手動或通過腳本為新文件設置權限,而不依賴默認 ACL。
在這種情況下,您可以省略
-d,只設置當前的 ACL 即可:sudo setfacl -m u:tomcat:rwx /var/www/html
範例:對比是否設置 -d 的效果
假設您在 /var/www/html 下創建了兩個文件:
-
沒有設置
-d的情況- 當您創建新文件時,不會繼承
tomcat的權限。 - 新文件的權限取決於當前的系統默認值(如
umask)。
- 當您創建新文件時,不會繼承
-
設置了
-d的情況- 新文件或目錄將繼承默認 ACL。例如,
tomcat將擁有對新文件的權限:結果顯示touch /var/www/html/newfile getfacl /var/www/html/newfiletomcat的權限被自動應用。
- 新文件或目錄將繼承默認 ACL。例如,
結論
- 需要持續新增文件/目錄並繼承權限:建議使用
-d設置默認 ACL。 - 僅對當前文件和目錄設置權限:可以省略
-d。
建議根據場景選擇適合的設置,以滿足項目需求。
【Linux】SSH 跳板機設定
如果你經常需要透過主機A跳到主機B進行操作,可以考慮以下幾種方法來提升效率:
1. SSH 跳板機設定 (ProxyJump 或 ProxyCommand)
你可以在 ~/.ssh/config 檔案中設定跳板機 (主機A) 和目標主機 (主機B)。
方法一:使用 ProxyJump (推薦,簡潔且較新)
Host 主機B
HostName <主機B的IP或主機名>
User <使用者名稱>
ProxyJump <主機A的使用者名稱>@<主機A的IP或主機名>
使用這個設定後,你可以直接用 ssh 主機B 連接到主機B,不需先登入主機A。
方法二:使用 ProxyCommand (舊版本支援)
Host 主機B
HostName <主機B的IP或主機名>
User <使用者名稱>
ProxyCommand ssh <主機A的使用者名稱>@<主機A的IP> -W %h:%p
這個設定同樣讓你直接 ssh 主機B 連接到主機B。
2. SSH 快速別名設定 (使用別名簡化命令)
你也可以在 ~/.bashrc 或 ~/.zshrc 中設定別名,直接執行一個指令來登入主機B。
alias sshB='ssh -J <主機A的使用者名稱>@<主機A的IP或主機名> <主機B的使用者名稱>@<主機B的IP或主機名>'
之後,你只需要輸入 sshB 就能登入主機B。
3. 使用 SSH Multiplexing (重複利用 SSH 連線)
如果你需要頻繁連線到同一個主機,可以開啟 SSH 連線複用功能,減少重複登入的時間:
在 ~/.ssh/config 加入以下內容:
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上存放私鑰:
ssh -A <主機A的使用者名稱>@<主機A的IP>
ssh <主機B的使用者名稱>@<主機B的IP>
或者在 ~/.ssh/config 啟用 Agent Forwarding:
Host 主機A
ForwardAgent yes
這樣就能直接將本地端的 SSH 金鑰轉發到主機A,並用來登入主機B。
5. 使用 tmux 或 screen (維持長時間工作環境)
如果你的操作過程需要持續很長時間,建議在主機A或B上使用 tmux 或 screen 來保持工作環境,這樣即使斷線也能恢復。
安裝並啟動 tmux:
tmux new -s session_name
之後可以斷開連線並重新附加:
tmux attach -t session_name
6. 使用 Ansible 自動化操作 (進階)
如果你有固定的操作流程,可以使用 Ansible 寫 Playbook,批量操作多個主機,包括跳板機情境:
- 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 的核心概念
-
Session(工作階段):
-
Window(視窗):
-
Pane(窗格):
安裝 tmux
Ubuntu 安裝 tmux
Ubuntu 官方倉庫提供了 tmux,可直接使用 apt 安裝:
sudo apt update
sudo apt install tmux -y
macOS 安裝 tmux
macOS 用戶可以透過 Homebrew 安裝:
brew install tmux
🔍 更新或移除 tmux
Ubuntu
- 更新:
sudo apt update && sudo apt upgrade tmux - 移除:
sudo apt remove tmux -y
macOS
- 更新:
brew upgrade tmux - 移除:
brew uninstall tmux
⌨️ 常用指令快速上手
| 功能 | 指令 / 快捷鍵 | 說明 |
|---|---|---|
| session | ||
| 啟動 tmux | tmux |
開啟一個新的 tmux session |
tmux new -s 名稱 |
開啟一個新的 tmux session 使用自訂名稱 | |
| 查看所有 session | tmux ls |
列出目前所有 tmux session |
| 切換 session | Ctrl-b s |
圖形化切換session (如果該session 內有多視窗,可按 -> 選擇換到該視窗) |
| 離開 tmux(保持運行) | Ctrl-b d |
分離 session,回到原本終端 |
| 離開 tmux(結束此session) | Ctrl-b x |
結束目前session,回到原本終端 |
| 重新連接 session | tmux attach -t 名稱 |
重新連接到未結束的 session |
tmux a |
重新連接上次中斷seeion | |
| 刪除 session |
|
刪除指定名稱session |
| 刪除所有 session |
|
|
| 重新命名 session | Ctrl-b $ |
當前session 改名 |
tmux rename-session -t 舊名稱 新名稱 |
修改指令 session 名稱 | |
| window | ||
| 建立新視窗 | Ctrl-b c |
建立一個新的視窗 |
| 切換視窗 | Ctrl-b w |
圖形化切換window |
Ctrl-b 視窗編號 |
||
Ctrl-b n / p |
切換到下一個/上一個視窗 | |
| 關閉視窗 | Ctrl-b & |
關閉當前視窗 |
| 重新命名 window | Ctrl-b , |
更名目前window |
tmux rename-window -t 舊名稱 新名稱 |
更名指定window | |
| Pane | ||
| 分割窗格(垂直) | Ctrl-b " |
垂直分割出新窗格 |
| 分割窗格(水平) | Ctrl-b % |
水平分割出新窗格 |
| 切換窗格 | Ctrl-b 方向鍵 |
在窗格間切換 |
| vi ~/.tmux.conf 加入set -g mouse on |
使用滑鼠切換 使用滑鼠調整分割線 |
|
| 關閉窗格 | Ctrl-b x |
|
| 與上一格窗格互換 | Ctrl-b { |
|
| 與下一格窗格互換 | Ctrl-b } |
|
| 顯示窗格編號 | Ctrl-b q |
|
| 指定窗格互換 | tmux swap-pane -s 1 -t 2 |
|
| 其他 | ||
| 命令模式 | Ctrl-b : |
進入命令模式,可執行 tmux 指令 |
| 配置設定檔 |
vi ~/.tmux.conf set -g mouse on :支持滑鼠 set -g prefix C-s :ctrl + b前缀改成ctrl + s |
|
當滑鼠模式關閉(set -g mouse off)時,作業系統提供的標準複製/貼上功能將如預期運作。
但滑鼠模式開啟(set -g mouse on)時,剪貼簿的功能會造成失效
下面的程式碼可以新增到您的 .tmux.conf 檔案中,以將m和M鍵分別綁定到「滑鼠開啟」和「滑鼠關閉」。
這讓你能夠使用Ctrl+ B,m來開啟滑鼠支援;Ctrl+ B,M將其關閉。
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'
🎯 常見使用情境
- 遠端連線不中斷: 即使 SSH 斷線,tmux session 仍會在伺服器上持續運行。
- 多任務同時進行: 在一個終端視窗內同時監控多個任務、Log 或服務狀態。
- 自動化腳本執行: 方便長時間執行的任務或背景作業管理。
常見問題
已在某session 內要新建session
或是要切換到另一個session
這代表 你已經在一個 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 名稱
以下是 tmux 快速鍵表格:
| 快速鍵 | 功能說明 |
|---|---|
| C-b Space | 選擇下一個佈局 |
| C-b ! | 將窗格分離到新視窗 |
| C-b " | 垂直分割視窗 |
| C-b # | 列出所有剪貼簿 |
| C-b $ | 重新命名當前工作階段 |
| C-b % | 水平分割視窗 |
| C-b & | 關閉當前視窗 |
| C-b ' | 輸入視窗索引以切換 |
| C-b ( | 切換到上一個客戶端 |
| C-b ) | 切換到下一個客戶端 |
| C-b , | 重新命名當前視窗 |
| C-b - | 刪除最近的剪貼簿 |
| C-b . | 移動當前視窗 |
| C-b / | 顯示按鍵綁定說明 |
| C-b 0~9 | 選擇對應編號的視窗 |
| C-b : | 輸入命令提示符 |
| C-b ; | 切換到上次使用的窗格 |
| C-b = | 從清單中選擇剪貼簿 |
| C-b ? | 列出所有按鍵綁定 |
| C-b C | 自訂選項 |
| C-b D | 從清單中選擇並分離客戶端 |
| C-b E | 平均分配所有窗格 |
| C-b L | 切換到最後一個客戶端 |
| C-b M | 清除標記的窗格 |
| C-b [ | 進入複製模式 |
| C-b ] | 貼上最近的剪貼簿 |
| C-b c | 建立新視窗 |
| C-b d | 分離當前客戶端 |
| C-b f | 搜尋窗格 |
| C-b i | 顯示視窗資訊 |
| C-b l | 切換到上個視窗 |
| C-b m | 標記或取消標記窗格 |
| C-b n | 選擇下一個視窗 |
| C-b o | 選擇下一個窗格 |
| C-b p | 選擇上一個視窗 |
| C-b q | 顯示窗格編號 |
| C-b r | 重新整理當前客戶端 |
| C-b s | 從清單中選擇工作階段 |
| C-b t | 顯示時鐘 |
| C-b w | 從清單中選擇視窗 |
| C-b x | 關閉當前窗格 |
| C-b z | 放大/還原當前窗格 |
| C-b { | 與上方窗格交換位置 |
| C-b } | 與下方窗格交換位置 |
| C-b ~ | 顯示訊息 |
| C-b DC | 重置視窗以跟隨游標 |
| C-b PPage | 進入複製模式並向上捲動 |
| C-b ↑/↓/←/→ | 選擇相對方向的窗格 |
| C-b M-1~M-7 | 設定不同的視窗佈局 |
| C-b M-n | 選擇下一個有警示的視窗 |
| C-b M-o | 反向循環切換窗格 |
| C-b M-p | 選擇上一個有警示的視窗 |
| C-b M-↑/↓/←/→ | 調整窗格大小 (每次5個單位) |
| C-b C-b | 傳送前綴鍵 |
| C-b C-o | 循環切換窗格 |
| C-b C-z | 暫停當前客戶端 |
| C-b C-↑/↓/←/→ | 調整窗格大小 |
| C-b S-↑/↓/←/→ | 移動視窗的可見部分 |
Reference
【Linux】【Ubuntu】 中更改 hostname
在 Ubuntu 中更改 hostname 有兩種常見方式,以下提供完整步驟:
1️⃣ 使用 hostnamectl(推薦方式,Ubuntu 16.04 之後)
hostnamectl 是 systemd 提供的指令,可同時修改 暫時 與 永久 主機名稱。
# 查看當前 hostname
hostnamectl status
# 修改 hostname,例如改成 my-server
sudo hostnamectl set-hostname my-server
-
立即生效:不需重開機,新的主機名稱會寫入
/etc/hostname並更新systemd。 -
驗證:
hostname hostnamectl
⚠️ 有時候 shell 提示字仍顯示舊名稱,重新登入 SSH 或
su - $USER會看到更新。
2️⃣ 手動編輯設定檔
如果是較舊版本或想手動修改,可直接編輯以下兩個檔案:
-
/etc/hostname
sudo nano /etc/hostname把原本的名稱改成新的,如
my-server。 -
/etc/hosts
sudo nano /etc/hosts找到類似:
127.0.1.1 old-hostname改成:
127.0.1.1 my-server這步驟避免本機解析錯誤。
-
重新啟動或執行:
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
一、確認目前磁碟架構
lsblk
範例輸出:
sda
├─sda1 1M BIOS boot
├─sda2 2G /boot
└─sda3 78G LVM → ubuntu--vg → ubuntu--lv → /
檢查磁碟實際大小:
sudo fdisk -l /dev/sda
確認 sda3 沒有覆蓋整顆磁碟(例如結束 sector 明顯在中段)。
二、調整分區(fdisk 擴大 /dev/sda3)
⚠️ 這步只修改分區表,不會刪資料。
關鍵是「Start sector 不變」,這樣 LVM 仍能識別。
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
三、重新讀取分區表
sudo partprobe
若出現 busy 警告,可忽略 /dev/sr0,只要 /dev/sda3 沒報錯即可。
如有報錯,可重開機一次再繼續。
四、擴大 LVM 實體卷(PV)
sudo pvresize /dev/sda3
檢查是否成功:
sudo vgdisplay
你應該看到:
Free PE / Size >0 / 約120GiB
五、擴大邏輯卷(LV)
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
六、擴大檔案系統(線上擴充)
先確認檔案系統類型:
df -Th /
結果可能是:
/dev/mapper/ubuntu--vg-ubuntu--lv ext4 ...
▸ 若是 ext4:
sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
▸ 若是 xfs:
sudo xfs_growfs /
七、驗證結果
df -h
預期結果:
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv 196G 20G 170G 11% /
✅ 狀態總結
| 項目 | 結果 |
|---|---|
分區 /dev/sda3 |
已擴大到整顆磁碟 |
PV (pvresize) |
已更新容量 |
VG / LV (lvextend) |
成功放大 |
檔案系統 (resize2fs) |
線上擴充完成 |
| 是否需重開機 | ❌ 不需要 |
💡 小提醒
-
如果修改的是
/boot或 EFI 分區,才需要重開機。 -
若
partprobe無法重新讀取分區表,可在安全狀態下 reboot。 -
這個流程對 Ubuntu / Debian / Proxmox VM 都適用。
【Linux】swap相關
從你貼的 top 看起來 Swap 使用率偏高(KiB Swap: 79.8/1044476 很像是 79.8% / 1,044,476 KiB),要「釋放 swap」本質上就是把已經換到 swap 的頁面搬回 RAM。最直接的方法是 swapoff → swapon(會短暫吃 RAM、可能卡頓一下)。
✅ 1) 先確認 RAM 夠不夠(很重要)
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(常用)
sudo swapoff -a
sudo swapon -a
清完再確認:
free -h
swapon --show
若你是 swapfile(例如
/swap.img或/swapfile),也可只針對單一檔案:
sudo swapoff /swap.img # 或 /swapfile
sudo swapon /swap.img
✅ 3) 找出「誰在吃 swap」(不然很快又會回來)
有 smem 最方便
sudo apt-get update && sudo apt-get install -y smem
sudo smem -rs swap | head
沒有 smem:用 /proc 快速抓(由大到小)
# 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 最方便:
watch -n 5 free -h
想加上時間戳一起看:
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 被你清掉了)。
✅ 如何判斷「到底有沒有影響」:
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 讀回來;目前沒有再把新的頁面換出去。
會有什麼影響?
-
尾延遲抖動(最典型)
只要si不為 0,就表示有 swap I/O;那段時間碰到這些頁面的 thread 可能會卡一下(但你wa=0表示目前 I/O 沒被堵住,所以感受可能不明顯或很輕微)。 -
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。
想看它真正對應到什麼名字(建議你查一下):
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(可選)
sudo sysctl -w vm.swappiness=10
要永久生效:
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)
等價於:
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
要清掉還是要靠:
swapoff -a && swapon -a
風險 / 代價是什麼?
-
如果 swappiness 設太低(例如 0 或 1),在記憶體壓力來時:
-
kernel 會更晚使用 swap
-
可能更快觸發 OOM kill(把某些程序殺掉)來保護系統
-
-
所以對「希望系統穩住不要 OOM」的場景,有時會保留比較高的值(例如 20~60),讓 swap 當緩衝。
你的機器 RAM 45G、swap 很小(520M~1G),把 swappiness 降到 10 通常是合理的偏好:少 swap、降低抖動。
這設定會持久嗎?
不會。重開機會還原。
要永久化:
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl --system
建議你怎麼驗證有沒有幫助?
改完後觀察:
vmstat 1
看 so(swap out)是否長期維持 0,系統是否少出現延遲抖動。
如果你願意貼 cat /proc/sys/vm/swappiness 以及你的服務類型(DB / API / ELK / Docker host),我可以給你更適合的 swappiness 建議值。
