計算機教育中缺失的一課 - MIT - L5 - 命令行環境

https://missing.csail.mit.edu/
https://missing-semester-cn.g...
https://www.bilibili.com/vide...

筆記

任務控制

shell 會使用 UNIX 提供的信號機制執行進程間通訊。當一個進程接收到信號時,它會中止執行、處理該信號並基於信號傳遞的信息來改變其執行。就這一點而言,信號是一種軟件中斷。php

結束進程

  • See man signal for reference
  • kill: sends signals to a process; default is TERM
  • SIGINT: ^C; interrupt program; terminate process
  • SIGQUIT: ^\; quit program
  • SIGKILL: terminate process; kill program; cannot be captured by process and will always terminate immediatelyhtml

    • Can result in orphaned child processes
  • SIGSTOP: pause a processpython

    • SIGTSTP: ^Z; terminal stop
  • SIGHUP: terminal line hangup; terminate process; will be sent when terminal is closedlinux

    • Use nohup to avoid
  • SIGTERM: signal requesting graceful process exitgit

    • To send this signal: 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

暫停和後臺執行進程

使用 fgbg 命令恢復暫停的工做。它們分別表示在前臺繼續或在後臺繼續。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)

管理配置文件的一個方法是,把它們集中放在一個文件夾中,例如 ~/.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 (Secure Shell)

# 鏈接設備
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 密鑰

基於密鑰的驗證機制使用了密碼學中的公鑰,咱們只須要向服務器證實客戶端持有對應的私鑰,而不須要公開其私鑰。這樣您就能夠避免每次登陸都輸入密碼的麻煩了祕密就能夠登陸。

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_rsaid_rsa.pub 兩個文件(或者 id_ed25519id_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 複製文件有不少方法:

  • ssh+tee, 最簡單的方法是執行 ssh 命令,而後經過這樣的方法利用標準輸入實現 cat localfile | ssh remote_server tee serverfile。回憶一下,tee 命令會將標準輸出寫入到一個文件;
  • scp :當須要拷貝大量的文件或目錄時,使用scp 命令則更加方便,由於它能夠方便的遍歷相關路徑。語法以下:scp path/to/local_file remote_host:path/to/remote_file
  • rsyncscp 進行來改進,它能夠檢測本地和遠端的文件以防止重複拷貝。它還能夠提供一些諸如符號鏈接、權限管理等精心打磨的功能。甚至還能夠基於 --partial標記實現斷點續傳。rsync 的語法和scp相似。

端口轉發

本地端口轉發 Local Port Forwarding

遠程端口轉發 Remote Port Forwarding

常見的情景是使用本地端口轉發,即遠端設備上的服務監聽一個端口,而您但願在本地設備上的一個端口創建鏈接並轉發到遠程端口上。例如,咱們在遠端服務器上運行 Jupyter notebook 並監聽 8888 端口。 而後,創建從本地端口 9999 的轉發,使用 ssh -L 9999:localhost:8888 foobar@remote_server 。這樣只須要訪問本地的 localhost:9999 便可。

SSH 配置

使用 ~/.ssh/config 文件來建立別名,相似 scprsyncmosh的這些命令均可以讀取這個配置並將設置轉換爲對應的命令行選項。

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:

常見的 Shell 框架:

終端模擬器

一些經典的模擬器:

一些新興的模擬器(一般具備更好的性能,例以下面兩個具備 GPU 加速):

課後練習

任務控制

習題 1

$ 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

習題 2

$ 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
相關文章
相關標籤/搜索