https://missing.csail.mit.edu/
https://missing-semester-cn.g...
https://www.bilibili.com/vide...
shell 會使用 UNIX 提供的信號機制執行進程間通訊。當一個進程接收到信號時,它會中止執行、處理該信號並基於信號傳遞的信息來改變其執行。就這一點而言,信號是一種軟件中斷。php
man signal
for referencekill
: sends signals to a process; default is TERMSIGINT
: ^C
; interrupt program; terminate processSIGQUIT
: ^\
; quit programSIGKILL
: terminate process; kill program; cannot be captured by process and will always terminate immediatelyhtml
SIGSTOP
: pause a processpython
SIGTSTP
: ^Z
; terminal stopSIGHUP
: terminal line hangup; terminate process; will be sent when terminal is closedlinux
nohup
to avoidSIGTERM
: signal requesting graceful process exitgit
kill -TERM <pid>
下面這個 Python 程序向您展現了捕獲信號 SIGINT
並忽略它的基本操做,它並不會讓程序中止。爲了中止這個程序,咱們須要使用 SIGQUIT
信號。程序員
#!/usr/bin/env python import signal, time def handler(signum, time): print("nI got a SIGINT, but I am not stopping") signal.signal(signal.SIGINT, handler) i = 0 while True: time.sleep(.1) print("r{}".format(i), end="") i += 1
$ python sigint.py 24^C I got a SIGINT, but I am not stopping 26^C I got a SIGINT, but I am not stopping 30^\[1] 39913 quit python sigint.py
使用 fg
或 bg
命令恢復暫停的工做。它們分別表示在前臺繼續或在後臺繼續。github
jobs
命令會列出當前終端會話中還沒有完成的所有任務。可使用 pid 引用這些任務(也能夠用 pgrep
找出 pid)。也可使用百分號 + 任務編號(jobs
會打印任務編號)來選取該任務。若是要選擇最近的一個任務,可使用 $!
這一特殊參數。shell
命令中的 &
後綴可讓命令在直接在後臺運行,不過它此時仍是會使用 shell 的標準輸出。vim
使用 Ctrl-Z
放入後臺的進程仍然是終端進程的子進程,一旦關閉終端(會發送另一個信號 SIGHUP
),這些後臺的進程也會終止。爲了防止這種狀況發生,可使用 nohup
(一個用來忽略 SIGHUP
的封裝) 來運行程序。針對已經運行的程序,可使用 disown
。瀏覽器
$ sleep 1000 ^Z [1] + 18653 suspended sleep 1000 $ nohup sleep 2000 & [2] 18745 appending output to nohup.out $ jobs [1] + suspended sleep 1000 [2] - running nohup sleep 2000 $ bg %1 [1] - 18653 continued sleep 1000 $ jobs [1] - running sleep 1000 [2] + running nohup sleep 2000 $ kill -STOP %1 [1] + 18653 suspended (signal) sleep 1000 $ jobs [1] + suspended (signal) sleep 1000 [2] - running nohup sleep 2000 $ kill -SIGHUP %1 [1] + 18653 hangup sleep 1000 $ jobs [2] + running nohup sleep 2000 $ kill -SIGHUP %2 $ jobs [2] + running nohup sleep 2000 $ kill %2 [2] + 18745 terminated nohup sleep 2000 $ jobs
終端多路複用使咱們能夠分離當前終端會話並在未來從新鏈接。這讓您操做遠端設備時的工做流大大改善,避免了 nohup
和其餘相似技巧的使用。
如今最流行的終端多路器是 tmux
。
會話 - 每一個會話都是一個獨立的工做區,其中包含一個或多個窗口
tmux
開始一個新的會話tmux new -s NAME
以指定名稱開始一個新的會話tmux ls
列出當前全部會話tmux
中輸入 <C-b> d
(detach),將當前會話分離tmux a
(attach)從新鏈接最後一個會話。您也能夠經過 -t
來指定具體的會話窗口 - 至關於編輯器或是瀏覽器中的標籤頁,從視覺上將一個會話分割爲多個部分
<C-b> c
建立一個新的窗口,使用 <C-d>
關閉<C-b> N
跳轉到第 N 個窗口,注意每一個窗口都是有編號的<C-b> p
(previous)切換到前一個窗口<C-b> n
(next)切換到下一個窗口<C-b> ,
重命名當前窗口<C-b> w
列出當前全部窗口面板 - 像 vim 中的分屏同樣,面板使咱們能夠在一個屏幕裏顯示多個 shell
<C-b> "
水平分割<C-b> %
垂直分割<C-b> <方向>
切換到指定方向的面板,<方向> 指的是鍵盤上的方向鍵<C-b> z
(zoom)切換當前面板的縮放<C-b> [
開始往回捲動屏幕。您能夠按下空格鍵來開始選擇,回車鍵複製選中的部分<C-b> <空格>
在不一樣的面板排布間切換擴展閱讀: 這裏 是一份 tmux
快速入門教程, 而這一篇 文章則更加詳細,它包含了 screen
命令。您也許想要掌握 screen
命令,由於在大多數 UNIX 系統中都默認安裝有該程序。
# colorls source $(dirname $(gem which colorls))/tab_complete.sh alias ls=colorls alias l="ls -lh" alias ll="ls -lAh" alias la="ls -lah" alias hz="history | fzf" alias mv="mv -i" alias cp="cp -i" alias mkdir="mkdir -p" # To ignore an alias run it prepended with \ls # Or disable an alias altogether with unalias unalias la # To get an alias definition just call it with alias alias l # Will print l='ls -lh'
管理配置文件的一個方法是,把它們集中放在一個文件夾中,例如 ~/.dotfiles/
,並使用版本控制系統進行管理,而後經過腳本將其 符號連接 到須要的地方。這麼作有以下好處:
一些技巧:
if [[ "$(uname)" == "Linux" ]]; then {do_something}; fi # 使用和 shell 相關的配置時先檢查當前 shell 類型 if [[ "$SHELL" == "zsh" ]]; then {do_something}; fi # 您也能夠針對特定的設備進行配置 if [[ "$(hostname)" == "myServer" ]]; then {do_something}; fi # Test if ~/.aliases exists and source it if [ -f ~/.aliases ]; then source ~/.aliases fi
# 鏈接設備 ssh foo@bar.mit.edu ssh foobar@192.168.1.42 # 若是存在配置文件,能夠簡寫 ssh bar # 執行命令 # 在本地查詢遠端 ls 的輸出 ssh foobar@server ls | grep PATTERN # 在遠端對本地 ls 輸出的結果進行查詢 ls | ssh foobar@server grep PATTERN
基於密鑰的驗證機制使用了密碼學中的公鑰,咱們只須要向服務器證實客戶端持有對應的私鑰,而不須要公開其私鑰。這樣您就能夠避免每次登陸都輸入密碼的麻煩了祕密就能夠登陸。
ssh-keygen -t ed25519 -C "_your_email@example.com_" # If you are using a legacy system that doesn't support the Ed25519 algorithm, use: ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
生成的 id_rsa 和 id_rsa.pub 兩個文件(或者 id_ed25519 和 id_ed25519.pub),分別爲的私鑰和公鑰。私鑰等效於你的密碼,因此必定要好好保存它。要檢查您是否持有某個密鑰對的密碼並驗證它,您能夠運行 ssh-keygen -y -f /path/to/key
。
ssh
會查詢 .ssh/authorized_keys
來確認那些用戶能夠被容許登陸。您能夠經過下面的命令將一個公鑰拷貝到這裏:
cat .ssh/id_ed25519.pub | ssh foobar@remote 'cat >> ~/.ssh/authorized_keys'
若是支持 ssh-copy-id
的話,可使用下面這種更簡單的解決方案:
ssh-copy-id -i .ssh/id_ed25519.pub foobar@remote
使用 ssh 複製文件有不少方法:
ssh+tee
, 最簡單的方法是執行 ssh
命令,而後經過這樣的方法利用標準輸入實現 cat localfile | ssh remote_server tee serverfile
。回憶一下,tee
命令會將標準輸出寫入到一個文件;scp
:當須要拷貝大量的文件或目錄時,使用scp
命令則更加方便,由於它能夠方便的遍歷相關路徑。語法以下:scp path/to/local_file remote_host:path/to/remote_file
;rsync
對 scp
進行來改進,它能夠檢測本地和遠端的文件以防止重複拷貝。它還能夠提供一些諸如符號鏈接、權限管理等精心打磨的功能。甚至還能夠基於 --partial
標記實現斷點續傳。rsync
的語法和scp
相似。本地端口轉發
遠程端口轉發
常見的情景是使用本地端口轉發,即遠端設備上的服務監聽一個端口,而您但願在本地設備上的一個端口創建鏈接並轉發到遠程端口上。例如,咱們在遠端服務器上運行 Jupyter notebook 並監聽 8888
端口。 而後,創建從本地端口 9999
的轉發,使用 ssh -L 9999:localhost:8888 foobar@remote_server
。這樣只須要訪問本地的 localhost:9999
便可。
使用 ~/.ssh/config
文件來建立別名,相似 scp
、rsync
和mosh
的這些命令均可以讀取這個配置並將設置轉換爲對應的命令行選項。
Host vm User foobar HostName 172.16.174.141 Port 2222 IdentityFile ~/.ssh/id_ed25519 LocalForward 9999 localhost:8888 # 在配置文件中也可使用通配符 Host *.mit.edu User foobaz
服務器側的配置一般放在 /etc/ssh/sshd_config
。您能夠在這裏配置免密認證、修改 shh 端口、開啓 X11 轉發等等。也能夠爲每一個用戶單獨指定配置。
鏈接遠程服務器的一個常見痛點是遇到由關機、休眠或網絡環境變化致使的掉線。若是鏈接的延遲很高也很讓人討厭。Mosh(即 mobile shell )對 ssh 進行了改進,它容許鏈接漫遊、間歇鏈接及智能本地回顯。
有時將一個遠端文件夾掛載到本地會比較方便, sshfs 能夠將遠端服務器上的一個文件夾掛載到本地,而後您就可使用本地的編輯器了。
常見的 Shell:
常見的 Shell 框架:
一些經典的模擬器:
一些新興的模擬器(一般具備更好的性能,例以下面兩個具備 GPU 加速):
$ sleep 1000 ^Z [1] + 689 suspended sleep 1000 $ sleep 2000 ^Z [2] + 697 suspended sleep 2000 $ jobs [1] - suspended sleep 1000 [2] + suspended sleep 2000 $ bg %1 [1] - 689 continued sleep 1000 $ jobs [1] - running sleep 1000 [2] + suspended sleep 2000 $ pgrep -af "sleep 1" 689 sleep 1000 $ pkill -f "sleep 1" [1] - 689 terminated sleep 1000 $ jobs [2] + suspended sleep 2000 $ pkill -f "sleep 2" $ jobs [2] + suspended sleep 2000 $ pkill -9 -f "sleep 2" [2] + 697 killed sleep 2000 $ jobs
參見 man kill
,默認發送的信號是 TERM。-9
等價於 -SIGKILL
或者 -KILL
$ sleep 10 & [1] 1121 $ pgrep sleep | wait ; ls [1] + 1121 done sleep 10 Nothing to show here
$ pidwait() { wait $1 echo "done" eval $2 } $ sleep 10 & [1] 1420 $ pidwait 1420 "ls" [1] + 1420 done sleep 10 done Nothing to show here