課程概覽與shell
動機
作為電腦科學家,我們都知道電腦最擅長幫助我們完成重複性的工作。但是我們卻常常忘記這一點也適用於我們_使用_計算機的方式,而不只是利用計算機程序去幫我們求解問題。在從事與計算機相關的工作時,我們有很多觸手可及的工具可以幫助我們更有效率地解決問題。但是我們大多數人實際上只利用了這些工具中的很少一部分,我們常常只是死記硬背地掌握了一些對我們來說如咒語一般的命令, 或是當我們卡住的時候,盲目地從網上複製貼上一些命令。
希望此課程可以協助你解決以上所提及的問題。
我們將講授如何善用你熟知的工具的全部功能,或為你提供全新的選擇,並且希望能引起你探索(甚至是開發)更多工具的興趣。我們認為這正是如今電腦課程所缺少的內容。
課程結構
此課程含有十一節約一小時的課程,每一個課程都會關注一個特定主題。儘管這些內容大致上是相互獨立的,但隨著課程的進行,我們會假定您已經掌握了之前的內容。每個課程都有在線筆記供查閱,但是課上的很多內容並不會包含在筆記中。因此我們也會把課程錄製下來發佈到網路上供大家觀看學習。
我們希望能在這11個鐘頭的課程中涵蓋大部分必要的內容,因此課程地節奏會比較緊湊。為了能幫助您以自己的節奏來掌握課程內容,每次課程都包含來一組練習來幫助您掌握該堂課的重點。 每堂課後,我們會安排時間回答問題。若你在線上參與課程,可以將問題發送至missing-semester@mit.edu。
由於時間有限,我們無法如同專門課程一樣涵蓋所有內容。我們會適時地將您介紹一些優秀的資源,幫助您深入的理解相關的工具或主題。但是如果您還有一些特別關注的話題,也請聯繫我們。
Topic 1: The Shell
Shell 是什麼?
如今,電腦有各種介面來讓我們輸入指令,精美的圖形化介面,語音輸入,甚至是AR/VR介面已經無處不在。這些介面可應用在80%的使用場景,但從根本上限制了你能做的事情————你無法點選不存在的按鈕或者用語音輸入從未收錄的指令。為了充分利用電腦的功能,我們不得不回到最根本的方式,使用純文字介面:Shell。
近乎所有你能接觸到的平臺都以某種形式支援shell,並且他們中的許多都提供了多種shell介面供你選擇。雖然它們之間有些細節上都差異,但是其核心功能都是一樣的:它允許你執行程式,輸入並獲取某種半結構化的輸出。
本節課我們會使用Bourne Again SHell, 簡稱 “bash”。這是使用最廣泛的shell之一,其語法與許多其他shell相似。開啟shell的 命令提示字元(可以鍵入指令的地方),需要先開啟 終端(terminal) 。你的裝置通常已經安裝了終端,或者你也可以選擇自己安裝一個,非常簡單。
使用shell
當你開始使用終端時,你會看到一個 提示字元,它通常看起來像這樣:
missing:~$
這是shell的主要文字介面,它告訴你, 你的主機名稱是 missing
並且「目前的工作目錄(directory)」,或者說是你目前的位置,是 ~
(表示「home」)。$
符號表示你現在不是 root 使用者 (稍後會介紹)。 在此提示字元中,你可以鍵入 指令 , 指令會被shell解析。最簡單的指令就是執行一個程式:
missing:~$ date
Fri 10 Jan 2020 11:49:31 AM EST
missing:~$
在此,我們執行了 date
程式,不出所料地,他印出了當前的日期與時間。然後shell將會等待我們執行其他指令。我們可以執行帶有 參數(arguments) 的指令:
missing:~$ echo hello
hello
上例中,我們告訴shell執行 echo
,同時帶有一參數 hello
。 echo
程式會將參數列印出來。shell基於空格對指令進行分割,然後執行第一個詞代表的程式,同時將後續的詞作為程式可以存取的參數。如果你希望傳入的參數中含有空格或者其他特殊字元(比如一個名為「My Photos」的檔案夾),你可以使用'
或 "
將其包裹起來,或使用跳脫字元 \
來處理 (My\ Photos
)。
但是shell如何知道怎樣找到 date
或 echo
程式?實際上,類似於 Python 或 Ruby,shell是一個開發環境,所以它具備變數,條件,迴圈,和函式(下一課講解)。當你在shell內執行指令時,你實際上在寫一段可以被shell解釋的程式碼。如果shell被要求去執行不是程式關鍵字的指令,它會去查詢 環境變數(environment variable) $PATH
,$PATH
會指出當shell收取某指令時,尋找對應程式的路徑。
missing:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
missing:~$ which echo
/bin/echo
missing:~$ /bin/echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
當我們執行 echo
指令時, shell會知道它應該執行 echo
, 然後它會在 $PATH
中根據該名稱搜索由 :
分隔的一系列目錄。當shell找到程式時,shell會執行它(假設此檔案是 可執行的(executable) ,之後會講解)。我們可以使用 which
程式來找出給定程式名對應的是哪個檔案。我們也可以繞過 $PATH
,透過直接指定程式的 路徑(path) 來執行該程式。
在shell中切換路徑
shell中的路徑是一組被分隔的目錄(directory)。在 Linux 與 macOS 上由 /
劃分,在 Windows 上由 \
劃分。在 Linux 與 macOS 上,路徑 /
是根目錄,所有目錄與檔案位列於此。在 Windows 上則每個磁碟區內都有一個根目錄(例如 C:\
)。我們預設你在此課程中使用Linux檔案系統。以 /
起始的路徑被稱為 絕對路徑,其他的路徑被稱為 相對路徑 。相對路徑是相對於當前工作目錄的路徑。我們可以使用 pwd
來檢視當前工作目錄,並且可以使用 cd
來改變它。在路徑中,.
代表當前工作目錄,而 ..
代表其上層目錄。
missing:~$ pwd
/home/missing
missing:~$ cd /home
missing:/home$ pwd
/home
missing:/home$ cd ..
missing:/$ pwd
/
missing:/$ cd ./home
missing:/home$ pwd
/home
missing:/home$ cd missing
missing:~$ pwd
/home/missing
missing:~$ ../../bin/echo hello
hello
注意,shell會一直提示我們當前工作目錄位置。你可以變更終端設定來顯示各種資訊,我們將會在下一課詳細介紹。
通常來說,當我們執行程式時,它會在當前目錄執行,除非我們指定了其他目錄。例如,程式通常會在當前位置搜尋檔案,並且在需要的時候在當前位置建立新檔案。
我們可以使用 ls
來檢視當前目錄下有哪些檔案:
missing:~$ ls
missing:~$ cd ..
missing:/home$ ls
missing
missing:/home$ cd ..
missing:/$ ls
bin
boot
dev
etc
home
...
除非我們在第一個參數指定目錄,ls
會印出當前目錄下的內容。許多指令接受旗標與選項(帶有值的旗標),旗標以 -
起始且可以改變指令的行為。通常,執行程式時使用 -h
或 --help
旗標(在Windows系統中使用 /?
),會印出說明文字,來告訴使用者有哪些旗標與選項可用。例如,ls --help
會告訴我們:
-l use a long listing format
missing:~$ ls -l /home
drwxr-xr-x 1 missing users 4096 Jun 15 2019 missing
它可以給出目錄與檔案更加詳細的資訊。首先,d
表示 missing
是一個目錄(directory)。其後由三個字元 (rwx
) 組成的三組字元組,分別表示了檔案擁有者(missing
),使用者群組(users
),與其他所有人所對其擁有的權限。-
代表該使用者未擁有相應權限。上面顯示的資訊表示,只有擁有者對 missing
檔案夾有寫(w
)的權限(例如新增與刪除其中的檔案)。為了進入某個檔案夾,使用者必須對其本身與其上層檔案夾擁有「搜尋」(表示為「可執行(execute)」:x
)權限。而為了列出其所有內容,使用者必須對其擁有「讀(read)」(r
)權限。對於檔案,權限是類似的。要注意近乎所有在 /bin
下的程式在最後一組都有 x
權限,代表著所有人都可以執行這些程式。
在此階段還有一些其他常見命令需要理解,如 mv
(移動或更名檔案), cp
(複製檔案), 和 mkdir
(建立新檔案夾)。
如果你想瞭解 更多 關於程式輸入輸出或參數的內容,嘗試使用 man
程式。它以一個程式名作為參數,然後展示此程式的 操作手冊 ,輸入 q
以退出此操作手冊。
missing:~$ man ls
程式間的連通
在shell中,程式擁有兩個主要「流(stream)」:輸入資料流與輸出資料流。當程式嘗試獲取資訊時,它們會從輸入資料流中獲取,當程式印出結果時,會將資訊輸出至輸出資料流。 通常情況下,一個程式的輸入輸出流都是你的終端。或者說,你的鍵盤作為輸入,你的螢幕作為輸出。但是,我們可以重新導向這些流!
最簡單的重新導向是 < file
與 > file
。它們可以讓你分別重新導向輸入輸出資料流到檔案:
missing:~$ echo hello > hello.txt
missing:~$ cat hello.txt
hello
missing:~$ cat < hello.txt
hello
missing:~$ cat < hello.txt > hello2.txt
missing:~$ cat hello2.txt
hello
你也可以使用 >>
來向檔案附加(append)內容。這種重新導向的真正亮點在於使用 管道(pipes) 。 |
允許我們將一個程式的輸出”鏈接”另一程式的輸入:
missing:~$ ls -l / | tail -n1
drwxr-xr-x 1 root root 4096 Jun 20 2019 var
missing:~$ curl --head --silent google.com | grep --ignore-case content-length | cut --delimiter=' ' -f2
219
我們會在資料預處理一課中講述更多利用管道的優點。
功能全面又強大的工具
在大部分類Unix系統中,有一個使用者非常特殊:「root」。你也許已經在上面的例子中見過了。root 使用者(幾乎)不受權限限制,可以建立,讀取,更新,和刪除系統中的任何文件。我們通常不需要以 root 使用者登入系統,因為太容易操作失誤而壞系統。取而代之的是使用 sudo
指令。如同它的名字一樣,它允許你用「su」(「super user」, 或者說 「root」)身份來「do」某些事情。當遭遇遇到拒絕訪問(permission denied)的錯誤時,我們通常需要以 root 身份來執行。此時也請再次確認您是真的要執行此操作!
有一件事情是只有 root 使用者才可以做,就是向掛載在 /sys
下的 sysfs
檔案系統寫入內容。sysfs
以檔案形式展示了許多核心參數(kernel parameters),所以你可以在系統運行時不藉助工具輕鬆改變系統核心。 請注意在 Windows 與 macOS 中沒有這個檔案
例如,筆記型電腦的螢幕亮度寫在 brightness
文件中,它位於
/sys/class/backlight
透過對該檔案寫入數值,我們可以改變螢幕亮度。你的第一個想法可能是:
$ sudo find -L /sys/class/backlight -maxdepth 2 -name '*brightness*'
/sys/class/backlight/thinkpad_screen/brightness
$ cd /sys/class/backlight/thinkpad_screen
$ sudo echo 3 > brightness
An error occurred while redirecting file 'brightness'
open: Permission denied
出乎意料的是,我們已經使用了 sudo
來執行指令,卻還是遭遇了一個錯誤!關於shell,我們需要瞭解一件重要的事:|
, >
, 與 <
等是 被 shell ,而不是被獨立的程式 執行 的。echo
等程式並不「知道」我們使用了 |
。它們只是從輸入中獲取資訊並將結果寫入到輸出中。在這種情況中,shell (權限為當前使用者)在執行 sudo echo
前就嘗試打開 brightness 檔案並且寫入。此時因為不是以 root 使用者執行,我們的操作被拒絕了。
理解這一件事後,我們可以這樣執行:
$ echo 3 | sudo tee brightness
因為 tee
程式開啟了 /sys
,而且 它 正以 root 使用者執行,因此權限運作正常。我們可以在 /sys
下做有趣又有用的事情了,例如改變系統 LED 的狀態(你的路徑可能會不一樣):
$ echo 1 | sudo tee /sys/class/leds/input6::scrolllock/brightness
下一步…
現在你已經可以使用 shell 完成一些基本任務了。你應該可以透過shell查詢感興趣的檔案並使用大部分程式的基礎功能。在下一課中,我們將會談談如何使用 shell 與其他命令列工具完成並自動執行更複雜的任務。
課後練習
- 在
/tmp
下新建一個名為missing
的文件夾。 - 查詢
touch
的手冊,請善用man
指令。 - 用
touch
在missing
文件夾中新建一個叫semester
的文件。 - 將以下內容逐行寫入
semester
文件:#!/bin/sh curl --head --silent https://missing.csail.mit.edu
第一行可能有點棘手,
#
在Bash中表示註釋,而!
即使被雙引號("
)包裹也具有特殊的含義。 單引號('
)則不一樣,此處利用這一點解決輸入問題。更多資訊請參考quoting手冊。 - 嘗試執行這個文件。例如,將該腳本的路徑(
./semester
)輸入到您的shell中並按Enter。如果程序無法執行,請使用ls
命令來獲取資訊並理解其不能執行的原因。 - 執行
sh
直譯器(interpreter),並以semester
作為第一個參數, 例如sh semester
。 為什麼這樣可以但./semester
卻不行? - 查看
chmod
的手冊 (例如,使用man chmod
命令)。 - 使用
chmod
讓./semester
指令可直接執行而不是輸入sh semester
。如何讓你的shell知道該程式應該透過sh
直譯? 查看shebang來了解其用途。 - 使用
|
和>
,將semester
文件輸出的最後更改日期資訊,寫入根目錄下的last-modified.txt
的文件中。 - 寫一段指令來從
/sys
中獲取筆記型電腦的電量資訊,或者桌上型電腦的CPU溫度。注意:macOS並沒有sysfs,所以mac用戶可以跳過這一題。
Licensed under CC BY-NC-SA.