課程概覽與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,同時帶有一參數 helloecho 程式會將參數列印出來。shell基於空格對指令進行分割,然後執行第一個詞代表的程式,同時將後續的詞作為程式可以存取的參數。如果你希望傳入的參數中含有空格或者其他特殊字元(比如一個名為「My Photos」的檔案夾),你可以使用'" 將其包裹起來,或使用跳脫字元 \ 來處理 (My\ Photos)。

但是shell如何知道怎樣找到 dateecho 程式?實際上,類似於 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 與其他命令列工具完成並自動執行更複雜的任務。

課後練習

  1. /tmp 下新建一個名為 missing 的文件夾。
  2. 查詢 touch 的手冊,請善用 man 指令。
  3. touchmissing 文件夾中新建一個叫 semester 的文件。
  4. 將以下內容逐行寫入 semester 文件:
    #!/bin/sh
    curl --head --silent https://missing.csail.mit.edu
    

    第一行可能有點棘手, # 在Bash中表示註釋,而 ! 即使被雙引號(")包裹也具有特殊的含義。 單引號(')則不一樣,此處利用這一點解決輸入問題。更多資訊請參考quoting手冊

  5. 嘗試執行這個文件。例如,將該腳本的路徑(./semester)輸入到您的shell中並按Enter。如果程序無法執行,請使用 ls命令來獲取資訊並理解其不能執行的原因。
  6. 執行 sh 直譯器(interpreter),並以 semester 作為第一個參數, 例如 sh semester。 為什麼這樣可以但 ./semester 卻不行?
  7. 查看 chmod 的手冊 (例如,使用 man chmod 命令)。
  8. 使用 chmod./semester 指令可直接執行而不是輸入 sh semester。如何讓你的shell知道該程式應該透過 sh 直譯? 查看shebang來了解其用途。
  9. 使用 |> ,將 semester 文件輸出的最後更改日期資訊,寫入根目錄下的 last-modified.txt 的文件中。
  10. 寫一段指令來從 /sys 中獲取筆記型電腦的電量資訊,或者桌上型電腦的CPU溫度。注意:macOS並沒有sysfs,所以mac用戶可以跳過這一題。

Edit this page.

Licensed under CC BY-NC-SA.