or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Do you want to remove this version name and description?
Syncing
xxxxxxxxxx
突破盲點的 bash 使用技巧
bash 是 linux 下最通用的互動式 shell。 在有圖形界面以前,shell 就是 unix 使用者認知中電腦的全部。 本課程將會介紹多種冷門的 bash 使用技巧, 包含迴圈、多工、互動使用技巧、腳本撰寫; 帶領聽眾重新理解 shell 的設計哲學。 其中腳本會以 sh 為主,而互動式技巧則會以 bash 為主。
講者介紹: gholk
shell 與通用程式語言
二者間的取捨
shell 的理念
每個程序都有 stdin stdout stderr , 可以傳入 argv ,繼承環境變數。 shell 的核心功能就是呼叫、組合各程序,達到目的。
除了依賴外部程序, shell 中也能自定義 函數 、 括號命令群組 , 函數也像程序一樣有 std stream argv 環境變數等功能, 而命令群組一樣有 std stream ,但缺少 argv 。
截取 stream 的一部份
在行處理上不適用
以行來處理會漏資料,我不知道為什麼。
只能回去用 read
管道的縮排應該怎麼寫?
管道符為最後一個字元時,可以省略跳脫結尾換行符的反斜線。
也有些人覺得應該要寫清楚, 顯式手動跳脫比較好
,就像某些 js 開發者硬要加分號一樣。那就摻在一起作成撒尿牛丸啊! 這好像是 gnu 風格的縮排? 不確定從哪看來的,但目前我覺得這種寫法最美觀。
為什麼要用 cat
cat 的好處,統一格式,一律把輸入的檔案放在最開頭。
如果不用 cat , 重道向的檔案慣例是寫在第一個命令的結尾處, 後面跟著的命令同樣位置卻是空的,造成不協調感。
重導向符的位置
但其實重導向符的位置不一定要在結尾, 要擺開頭、中間、結尾都可以。
所以可以寫成這樣,夠反人類吧? 有沒有忽然覺得 cat 比較好看了?
如果你記得命令群組的用法,那也可以用在這裡:
有點像是沒有宣告成函數的寫法:
同一個檔案不能同時是管道的輸入與輸出
因為
>
會清空檔案內容,讓輸入讀不到資料。組合出一對多的管道
在複雜的情況時,會需要組合多個輸入輸出; 所以為了模擬複雜的情況,介紹幾個程式:
paste
逐行合併二個檔案
座標轉換程式 proj4
stdin 只有一個
有時,我們想把多個結果併在同一個檔案裡, 可以用 paste 把二個檔案的每一行串接起來。
但如果想要一次併好幾個檔案呢?
很遺憾,你只有一個 stdin 可以用。
process substitution
這個功能可以讓你把某個命令當作檔案寫入或讀取。 寫入是
>( )
,讀取是<( )
。進程替換其實就是把該部份語法, 置換成連結到該命令的管道。
那結合二者,就能把多個程序的輸出結合在一起了!
只是有個小問題,process substitution 是 bash 的專屬功能,sh 不支援。
tee and fifo
所以如果要在 sh 中執行,那得再介紹 fifo。 fifo 搭配 tee 就能實現一到多的管道。
fifo
fifo 其實是解決輸入輸出流沒有名字的問題。 因為 paste 需要多個輸入,但只用 stdin 只有一個, 所以用多個 fifo 來連接輸入輸出。
要實現一對多,最關鍵還是 tee; 其實 tee 也可以用
>( )
pipe 給多個程式。tee 與 fifo 的阻塞
tee 與 fifo 都是會阻塞的操作。 對 fifo 讀取或寫入時, 如果沒有其它程式同時在寫入或讀取,就會阻塞。
tee 也是會阻塞,如果 tee 寫入的任一個程序 或是檔案阻塞,那 tee 所有的輸出都會阻塞。
server 當自己家用
ssh 不一定是執行一個互動式的 shell , 也可以直接執行命令。
如果有一台以上的電腦,但某些程式只裝在特定一台, 可以用 ssh 幾乎無縫接軌取用。
善用別名
ssh 可以用別名代表一台伺服器, 不然每次都要打一長串帳號域名,一點都不像自己家。
自動壓縮或手動壓縮
如果資料量比較大時,可以考慮壓縮加速:
每次都手動壓也很累,不如直接在 ssh 層使用自動壓縮吧:
善用 stdin
但 ssh 執行遠端電腦上的程式的問題是,只有 stdin 能用。 如果真得要傳複數檔案,可以考慮用 tar 來打包, 但就得執行一長串命令來解開再打包回來了; 還要注意不要丟任何東西到 stdout,不然回來的 tar 會壞掉。 (只能靠 stderr 來 log 了。)
傳送單個 tar 封存:
傳送 tar 封存並執行達端程式,再把結果用 tar 送回來:
ssh key
只是你可能需要用 ssh-key 免密碼登入, 才能達到全家就是你家的方便等級。 如果怕安全問題,可以手動修改 server 上的
~/.ssh/authorized_keys
,用完就註解掉該行。ssh control master
或是用 ssh control master 登入一次後 就複用既有的 ssh 連線。 除了在連線存在期間不用重覆登入外, 還可以省下建 tcp 連線的時間。
當然,第一次登入還是要密碼, 所以如果要完全自動化,還是得用 ssh key。
只是用 control master 之後, 不管同時執行了幾個 ssh , 都只會跑在同一個 ssh 連線上。
用 disown 讓程式在登出後繼續運行
放棄難用的 nohup
nohup 只能用在執行檔上。 但如果你要用 sh 就另當別論了, disown 只能在 bash 中使用。
其實在迴圈的 done 後面直接加
&
就能丟到背景了,關於
&
的意義,其實比較像;
。加括號是有時要一次執行好幾個命令, 要全部丟到背景就可以用括號包起來成群組, 再把群組丟到背景。
迴圈寫法
有些命令適合放 while 後面。
比較醜,但比較好理解的寫法。
我也常用 sleep :
平行處理壓縮時間
同時處理二個
我有一個大膽的想法
比較保險的平行處理,直接跑二個迴圈。
編輯以前的命令並執行
很多人應該都知道可以用 ↑ ↓ 來執行以前的命令。
在歷史記錄中搜尋
另一個更好用的是 readline 的 C-r (reverse search) 可以搜尋以前的命令,不用 C-b 一個個找按半天。
C-r
C-r
換下一個匹配的,C-c
取消。fc: fix command
這個命令可以開啟編輯器編輯上一個執行的 shell 命令, 編輯完離開後就會執行。 可以用選項控制要編輯第 n 個命令,或是像
C-r
一樣搜尋。但其實 fc 不好用,因為不太可能 記住 要改的命令是第幾個, 而且搜尋匹配的第一個結果不一定是你印象中的。
直接開啟編輯器
另一個 readline 快捷鍵是
C-x C-e
(edit command in editor) , 是直接開啟編輯器編輯目前打到一半的 shell 命令。 主要用在命令打得很長的時候, 只靠 shell 基礎的功能編輯會很痛苦。編輯器同 fc 預設都是 vi 或看 EDITOR 變數。 所以請至少有一個能在 shell 中使用的編輯器。
取代 fc 的功能
搭配
C-r
搜尋的話,就搜到了再按C-x C-e
編輯即可, 互動搜尋的效果會比 fc 盲搜的結果好很多。為什麼多行命令被壓成一行?
問題在 cmdhist 與 lithist 這二個選項。
多行命令
only enable cmdhist
long multi-line command
~:$ for id in $(tail -n +2 csrs.id); do echo $id; curl-csrs-ppp get $id > $id.zip; basename=$(basename $(unzip -l $id.zip | awk '$4 ~ /mari.*pdf/ { print $4 }') .pdf); mv $id.zip $basename.19o.zip; unzip $basename.19o.zip $basename.csv; sleep 20s; done
enable lithist
lithist 故障
lithist 有時候會壞掉,多行命令會被分開成一行一行, 可能是因為舊的歷史檔案
~/.bash_history
格式亂掉。 像如果歷史檔案的大小超過限制會被截斷,格式就會亂掉。 修正或直接刪掉就會正常。edit function, alias, script
https://github.com/GHolk/loco/blob/master/bash_function#L78
在腳本中啟動另一個子 shell
當你需要在腳本內在另一支程式內執行一系列命令, 一般是要寫到另一個檔案直接執行。
但有時候不希望多一個檔案,管理起來會很麻煩, 會想要都寫在同一個同案裡。
here doc
比較直覺也比較保險的作法是用 heredoc, 但缺點是變數會被展開,可能需要跳脫。 (如果你的 IDE 有跳脫的快速鍵就不成問題。)
用 tail 抓出自己的內容
主要是用
$0
會指向檔案本身的技巧,事先算好行數, 但$0
會存完整路徑是 bash 的擴充, sh 中$0
只會存最終檔名。這是 debian 裡 grub-mkconfig 的做法, 因為 grub 腳本中用到了大量的變數,如果一一跳脫可讀性會很差。
用 sed 定位 exit
不用手動計算行數,但要注意不要匹配到奇怪的東西。
冷門用法
摺疊
格式化輸出類似的字串
重覆輸出
yes 的真正用途
當某些程式執行時會問很多 yes no 的時候, 用 yes 告訴他。
ex 批次編輯
雖然 sed 也可以批次編輯, 但有些功能還是要用可以來回跳躍的真正的編輯器比較方便, 而且編輯器還是比較快。
後來發現其實差不多,當檔案太大時,都是卡在硬碟寫入瓶頸。
為什麼不是 ed ?
有 vi 就有 ex
ex 的好處是,太新的發行版不一定有裝 ed , 但一定有裝 vi ,有 vi 就有 ex 。