如 除舊 + 佈新 + 回顧,為什麼 blog 又得再搬一次家? 中提到,在 ChatGPT 如此盛行之後,當年的技術筆記無論對讀者還是對自己,多數都已經一文不值。
不過去蕪存菁後,一些 snippets 以及自己過去的軌跡,還是可以多少搬移一些過來。
2021
用 sed
upsert 設定檔
2021-01,因為要更改大量 server 的網卡設定,於是包了一個小 function
# 將上面提到的指令改寫成函數,方便重複使用
add_setting (){
TARGET_FILE=$1
OPTION=$2
VALUE=$3
grep -q "^$OPTION" $TARGET_FILE && sed -i "s/^$OPTION.*/$OPTION=$VALUE/" $TARGET_FILE || echo "$OPTION=$VALUE" >> $TARGET_FILE
}
# 因此,修改網卡的工作就變成
add_setting $IF_FILE_PATH IPADDR 192.168.0.123
add_setting $IF_FILE_PATH GATEWAY 192.168.0.254
# 假設網卡檔案路徑已經存在 $IF_FILE_PATH 變數中
寫到這邊才發現方格子的 code block 居然沒有 shell / bash 的 style 選項!肯定是這裡的宅宅太少了。看來這篇所有的 bash snippets 都只能用 python style。
節錄翻譯:All about {Curly Braces} in Bash (Bash 裡所有關於大括號的事)
source: https://www.linux.com/topic/desktop/all-about-curly-braces-bash/
2021-11 開始開發比較多的 shell scripts,也順手紀錄了這篇。現在多數內容直接問 ChatGPT 就可以產生了,不過還是有些東西挺有趣的,像是:
Array builder:
dec2bin=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1})
echo ${dec2bin[25]}
# 00011001
Parameter expansion
# 把所有 jpg 轉成 png
for i in *.jpg; do
convert $i ${i%jpg}png;
done
%
是切後面,#
是切前面的字串。當初剛看到時覺得很難記,後來看鍵盤 #
的確在 %
的前面,就覺得簡單多了。
Output grouping
{
echo "I found all these PNGs:";
find . -iname "*.png";
echo "Within this bunch of files:";
ls;
} > PNGs.txt
這個小技巧平時似乎用不到,但在幾年後 GHA 盛行的現在,在 YAML 中定義把結果更新到 summary 時,會簡潔不少。
Process 資源排序
同樣在 2021-11,似乎是在排查客戶端機器的 ELK agent (filebeat, auditbeat ...) 資源使用問題時紀錄的,主要的目標是找出資源最高用量的 process 們。
# Memory
ps -o pid,user,%mem,command ax | sort -b -k3 -r | head
# CPU
ps -o pid,user,%cpu,command ax | sort -b -k3 -r | head
2022
iconv
: 將檔案轉成不同編碼 (encoding)
2022-04,在客戶端的 windows 日誌檔搬移到 linux 裡面處理,結果變成亂碼。
主要是因為那邊 windows 使用的是 big5 編碼,因此要用 iconv
指令做調整:
cat dns_xxx.log | iconv -f big5 -t utf8 -c
印象中應該是一些對方公司內部 DNS 的 log 檔,具體細節我已經忘記了,不過有 log 檔的話,如果要仔細比對,配合其他資訊可以大略知道公司內部 (IP) 的上網使用狀況,也是滿嚇人的。
關於 UTF-8 可以看 UTF-8 天天用,但你知道他到底怎麼編碼的嗎?
Amazon linux 調整時區
2022 年中,初次開始嘗試在 AWS 租用主機,準備來開發網頁服務。那時還沒開始做 Decents,做的是比較低調的台股報價網頁,不然一般的行情網頁花花綠綠,其他人經過位子一定馬上就發現在看報價。
前後端都做了雛形出來,結果發現為什麼早上開盤的時間都拿不到報價?
查了半天發現是我的 EC2 時區是 UTC,而程式沒有判斷時區,直接拿來用,才導致根本沒有觸發抓資料的函數。
印象中當時似乎一般的 timedatectl
也不管用,可能因為是 Amazon Linux 的緣故:
sudo timedatectl set-timezone Asia/Taipei
於是也順手紀錄的一篇後來有效的做法:
# Amazon Linux
sudo rm /etc/localtime
sudo ln -s /usr/share/zoneinfo/Asia/Taipei /etc/localtime
SSH 遠端執行函數
2022-08,那時候在更新前人留下來的 CD (Continuous Deployment) 內容,其中有一個步驟是要在遠端做一些工作。
我們知道,如果要在遠端執行簡單的指令可以直接放在 ssh
之後:
# 確認遠端主機的時間
ssh <remote server> "date"
# 確認遠端主機的內部 IP
ssh <remote server> "hostname -I"
但如果要做的事情比較複雜,就只能包成 function 處理了。這時候要在遠端執行之前,還需要額外 declare -f
才能順利生效:
remote_work () {
echo "hostname: $(hostname)"
date
whoami
# ... etc
}
# 必須額外先做 declare -f 宣告函數
ssh <remote server> "$(declare -f remote_work); remote_work"
vim 的常用設定
2022-08,終於紀錄了一篇 vim 的小抄,可以在接觸不同 server 時,如果對各個環境中的 vim 設定不滿,可以有地方複製自己習慣的設定[1]。
# vim ~/.vimrc
set tabstop=2
set shiftwidth=2
set softtabstop=-1
set expandtab
set hlsearch!
nnoremap <CR> :noh<CR><CR>
Kubernetes: 強迫刪除 Namespace
2022-10,工作上比較常管理 Kubernetes,不時會發現有時要刪除 namespace 時會卡住,即使用 --force --grace-period=0
強迫刪除也沒有用,因不明原因 finalizer
似乎沒有正常運作。
此時可以編輯 yaml 檔,把 finalizer
相關的內容清除,或是直接包成 function 使用
# 刪除 namespace 前可以先刪除相關資源
# kubectl delete all --all -n <namespace>
force_delete_namespace () {
TMP_NAMESPACE=$1
echo '{
"apiVersion": "v1",
"kind": "Namespace",
"metadata": {
"name": "_target_replace_ns_"
},
"spec": {
"finalizers": [
]
}
}' | sed "s/_target_replace_ns_/${TMP_NAMESPACE}/g" \
| kubectl replace --raw "/api/v1/namespaces/${TMP_NAMESPACE}/finalize" -f -
}
# force_delete_namespace <namespace>
在 Mac 上可以鍵盤按著重複輸入
似乎預設的設定是當按鍵按著的時候,會顯示其他特殊字元;不過通常我都是需要在 terminal 中重複輸入這些字,這可以修改設定達成:
defaults write -g ApplePressAndHoldEnabled -bool false
如同 vim,bash history
也紀錄了常用設定
2022-12 也是因為各地 history 的行為不太一樣,讓我決定整理一篇喜歡的設定方便複製[1]:
bash
# vim ~/.bashrc
HISTCONTROL=ignoreboth
HISTIGNORE="&:ls:[bf]g:exit:pwd:clear:history:[ \t]*"
zsh
setopt APPEND_HISTORY
setopt HIST_IGNORE_DUPS
HISTORY_IGNORE="(ls(| *)|cd|pwd|exit|cd ..)"
原先文章寫了很多補充說明,現在看起來都覺得,如果想了解更多只要貼給 ChatGPT 就好了。
清除 terminal 的所有文字
2022-12,不管是測試新語言,還是用 bash 看台股報價,要刷新 terminal 的文字總是需要用到一些控制碼。通常的做法是
clear && printf '\e[3J'
不過既然記了筆記,就是要盤點更精簡又更詳細更宅的說明,不能只有我看到:
- 形如
<ESC>[
稱為 Control Sequence Introducer (ref 1, ref 2),代表要對 terminal 做額外的控制。 - 而
<ESC>
本身有三種寫法:\33
,\033
,\e
,這個對應的都是 ASCII 第 27 碼 (033
是 8 進位表示的 27)。 3J
:3
是參數、J
代表 Erase Display (ref 3)。
有的時候不是所有環境都能下 clear
指令,例如在程式碼中。還好他也有對應的控制碼 \ec
,這代表的是 reset device (ref 4)。所以其實整件事可以寫成
printf "\ec\e[3J"
現在大家都看得懂了
\ec\e[3J
^^ ^^
└┴─┴┴───── <ESC>,你也可以改成 \33
\ec\e[3J
^^^
└┴┴─────── reset device,等同 clear 指令
\ec\e[3J
^^^
└┴┴──── Control Sequence Introducer: <ESC>[
\ec\e[3J
^^^^^
└┴┴┴┴── 清除 terminal
相信現在 Ansi color (例如 PTT 會用到的顏色碼) 也沒有這麼嚇人了
\e[0;30m Black
\e[0;31m Red
\e[0;32m Green
...
現在我也知道為什麼 blog 流量會這麼低了。
2023
Python 中取得 unix timestamp 哪個方式更快?
結論是 time.time()
比 datetime.now().timestamp()
快了 5~6 倍
>>> timeit.timeit("datetime.now().timestamp()", number=100000, setup='from datetime import datetime')
0.09604033301002346
>>> timeit.timeit("time.time()", number=100000, setup='import time')
0.017747000005329028
不過跑 100,000 次才看得出這種差異,所以其實挑你喜歡的語法就可以了。
大增 grep
的效率
轉眼到 2023-09,那時要從大量日誌中排查某問題可能的發生原因以及相關線索,原本只是下個 grep
從上百 MB 的檔案中過濾關鍵字,結果發現大概需要等個幾秒鐘,太久了!
於是經過一番研究,最後
LC_ALL=C grep -F -m1 <text> <input_file>
主要是日誌檔都是英文數字,所以不用 UTF-8 只用 ASCII 編碼查找、加上不用比對全部結果,這樣搭配參數直接快上一大截!
2024
curl
也能獲得回應時間
平時 curl
都用來測試連線,或是讀取 http headers,但他其實也能測試回應時間。起手式就是包成 function,太方便了:
curl_time() {
curl -so /dev/null -w "\
namelookup: %{time_namelookup}s\n\
connect: %{time_connect}s\n\
appconnect: %{time_appconnect}s\n\
pretransfer: %{time_pretransfer}s\n\
redirect: %{time_redirect}s\n\
starttransfer: %{time_starttransfer}s\n\
-------------------------\n\
total: %{time_total}s\n" "$@"
}
# curl_time "vocus.cc"
如果你是用 mac,也可以在 terminal (終端機) 中貼上 function 做類似的測試。
像是剛才對方格子 vocus.cc
的測試結果:
curl_time vocus.cc
namelookup: 0.031946s
connect: 0.077791s
appconnect: 0.000000s
pretransfer: 0.078097s
redirect: 0.000000s
starttransfer: 0.229098s
-------------------------
total: 0.229437s
Shell script 中引用了其他有互動的腳本,要指派 /dev/tty
我們知道某些指令可以提供互動功能,例如在真的做出下一步之前,要求使用者確認:
# 覆蓋檔案時先確認
mv -i new.txt old.txt
# 手動製作確認
read -p "Enter y: " var
如果你有另一個 shell script,會在裡面執行另一個 shell script,而且這個 script 是帶有互動功能 (interactive) 的,那就需要額外對 stdin
指定 /dev/tty
。
#!/bin/bash
do_job () {
another_script.sh </dev/tty
# ^^^^^^^^^ 這裡
}
創建十萬個空檔案,echo
還是 touch
比較快?
# touch: 33s
time for i in {0..10000}; do touch ${i}.txt; done
real 0m33.610s
user 0m5.049s
sys 0m29.588s
# echo: 0.9s
time for i in {0..10000}; do echo > ${i}.txt; done
real 0m0.943s
user 0m0.200s
sys 0m0.674s
用 echo >
的方式似乎比 touch
快了 35 倍!
這是因為用這種寫法,每次執行 touch
都要 fork
一次新的 process,而 echo
是 built-in command,不必 fork
就會快非常多。
所以如果能重複使用一個 touch
process,反而會比 echo
快一點:
# touch: 0.5s
time find ../ -name "*.txt" -printf '%P\n' | xargs touch
real 0m0.517s
user 0m0.028s
sys 0m0.425s
不安裝套件,手動製作 CPU Spike
9 月為了測試「CPU 負載變高要發出告警」的監控項目,後來用了簡單的內建指令達成:
seq $(nproc) | xargs -P0 -n1 sha256sum /dev/zero
多加上一個 timeout
指令可以讓他自動停止:
seq $(nproc) | xargs -P0 -n1 timeout 300 sha256sum /dev/zero
用 ccrypt
加密檔案
11 月為了把一些相對機敏的字串放在雲端硬碟,研究了一番決定用這個簡單的指令加密之後再放上去,這樣就可以有地方歸檔,內容也相對安全。
# 加密
ccrypt -e -K "${password}" <secret.txt >secret.encrypted.cpt
# 解密
ccrypt -d -K "${password}" <secret.encrypted.cpt
不過真的機敏的內容建議還是物理保存,例如放在便條紙或筆記本上,這樣就不可能被遠端破解。
2025
從 2025 開始,就全力開發分帳[2],也受益於 ChatGPT 的與時俱進,越來越多問題直接問他就可以獲得完整的答案,因此小的技術 snippets 數量銳減,整理一番發現也沒有可以放過來的內容了。
Notes
- 現在搬移到這邊,反而覺得想重複使用的話,應該要放在 Github Gist。
- 我開發的分帳服務:https://decents.me,新功能持續擴增中,希望能幫助到你與你的親友。