章 1. GNU/Linux 教學

# 章 1. GNU/Linux 教學

我想，當你學習一個電腦系統，就像學習一門新的外語。雖然教學和文檔是有幫助的，但你必須自己練習。爲了幫助你順利的開始，我詳細說明一些基本要點。

Debian GNU Linux中最強大的設計來自Unix作業系統，一個多人多工的作業系統。你必須學會利用這些特性以及Unix和GNU/Linux的相似性。

別迴避面對Unix的文檔，不要只是依賴於GNU/Linux文檔，這樣做會剝奪你瞭解許多有用的資訊。

| 注意
如果你在任何類Unix系統中使用過一段時間的命令列工具，你可能已經掌握了這份文檔中的內容。那請把它當做一個實戰檢驗和進修。

## 1.1. 控制臺基礎

### 1.1.1. shell 提示字元 (prompt)

啟動系統之後，如果你沒有安裝 GUI（例如GNOME 或者 KDE），那麼你會看到字元登入介面。假設你的主機名為`foo`，那麼登入提示符將如下所示。

如果你安裝了一個 GUI 環境，那麼你仍然能夠用 Ctrl-Alt-F3 進入基於字元的登入提示符，同時你能透過 Ctrl-Alt-F2 回到 GUI 環境（更多詳情請參閱下文 節 1.1.6, “虛擬控制檯”）。

    foo login:

在登錄提示字元下，你輸入你的使用者名，例如`penguin`，然後按 Enter 鍵，接下來輸入你的密碼並再次按 Enter 鍵。

| 注意
遵循Unix傳統，Debian系統下的使用者名和密碼是大小寫敏感的。使用者名通常由小寫字母組成。第一個使用者帳號通常在安裝期間進行創建。額外的使用者帳號由root使用者用 `adduser`(8)創建。

系統以保存在 "`/etc/motd`" 中的歡迎資訊（Message Of The Day）來開始，同時顯示一個指令提示字元。

    Debian GNU/Linux 12 foo tty3

    foo login: penguin
    Password:

    Linux foo 6.5.0-0.deb12.4-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.5.10-1~bpo12+1 (2023-11-23) x86_64

    The programs included with the Debian GNU/Linux system are free software;
    the exact distribution terms for each program are described in the
    individual files in /usr/share/doc/*/copyright.

    Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
    permitted by applicable law.

    Last login: Wed Dec 20 09:39:00 JST 2023 on tty3
    foo:~$

現在，你就在 shell 下。shell 解析你的指令。

### 1.1.2. GUI 下的 shell 提示符

如果你在安裝 Debian 的過程中，安裝了一個 GUI 環境，那麼你在啟動系統後將使用圖形登入介面。輸入你的使用者名稱和密碼可以登入到非特權使用者帳號。使用 Tab 鍵（跳格鍵）可以在使用者名稱和密碼之間移動，也可以使用滑鼠主要鍵點選。

要在 GUI（圖形使用者介面）環境下獲得 shell 提示符，你必須啟動一個`x 終端模擬器`程式，例如`gnome-terminal`(1)、`rxvt`(1)或`xterm`(1)。在 GNOME 桌面環境下，你可以按 超級鍵（Windows 鍵），在搜尋提示裡輸入"terminal"來開啟終端。

在其它一些桌面系統（如 `fluxbox`）下面，可能沒有明顯的開始選單入口。如果是這種情況，試下右擊桌面螢幕並希望能有彈出選單。

### 1.1.3. root 帳號

root 帳號也被稱作超級使用者或特權使用者。用這個帳號，你能夠履行下面的系統管理任務。

- 讀、寫和刪除系統上的任何文件，不顧它們的文件權限

- 設置系統上任何文件的所有者和權限

- 設置系統上任何非特權使用者的密碼

- 免使用者密碼登錄任何使用者

root 帳號擁有至高無上的權力，你要慎重和負責任的使用。

| 警告
從來不和其他人共享 root 密碼。

| 注意
一個文件（包括硬體設備，如CD-ROM等，這些對Debian系統來說都只是一個文件）的權限可能會導致非root使用者無法使用或存取它 。雖然在這種情況下，使用root帳戶是一個快速的方法，但正確的解決方法應該是對文件權限和使用者組的成員進行合適的設置（參見節 1.2.3, “檔案系統權限”）。

### 1.1.4. root shell 提示字元

這裏有一些基本的方法可以讓你在輸入root密碼後獲得root的shell提示字元。

- 在字元登入界面使用`root`作爲使用者名稱登入。

- 在任意使用者的shell提示字元下輸入“`su -l`”。

  - 這不會保存當前使用者的環境設定。

- 在任意使用者的shell提示字元下輸入“`su`”。

  - 這會保存當前使用者的一些環境設定。

### 1.1.5. GUI系統管理工具

如果你的桌面選單沒有使用適當許可權啟動 GUI（圖形使用者介面）的自動化管理工具，你可以在終端模擬器（例如`gnome-terminal`(1)、`rxvt`(1)或`xterm`(1)）中 root 的 shell 提示符下啟動它。參見節 1.1.4, “root shell 提示字元”和節 7.9, “X 服務端連線”。

| 警告
永遠不要在顯示管理器（例如`gdm3`(1)）的提示符下輸入`root`來使用 root 賬戶啟動 GUI 顯示/會話 管理器。 永遠不要在顯示關鍵資訊的X Window下運行不受信任的遠程GUI程序，因爲它可能會監聽你的X螢幕。

### 1.1.6. 虛擬控制檯

在預設的Debian系統中，有6個可切換的類VT100字元控制檯，可以直接在Linux主機上啓動shell。除非你處於GUI環境下，否則你可以同時按下`左Alt鍵`和`F1`—`F6`之一的鍵在虛擬控制檯間切換。每一個字元控制檯都允許獨立登入帳號並提供多使用者環境。這個多使用者環境是偉大的Unix的特性，很容易上癮。

如果你處於 GUI 環境中，你可以透過 `Ctrl-Alt-F3` 鍵前往字元控制檯 3，也就是同時按下`左 Ctrl 鍵`、`左 Alt 鍵`和`F3 鍵`。你可以按下 `Alt-F2` 回到 GUI 環境，它一般執行在虛擬控制檯 2。

你也可以使用命令列切換到另一個虛擬控制檯，例如切換到控制檯 3。

    # chvt 3

### 1.1.7. 怎樣退出命令列提示字元

在命令列輸入`Ctrl-D`，即同時按下`左側-Ctrl-鍵`和`d-鍵`，即可關閉 shell 活動。如果你正處於字元控制檯，你將會回傳到登錄提示行。儘管這些控制字元 “control D" 使用了大寫字母，你並不需要按住 Shift-鍵。`Ctrl-D` 也可以簡寫爲 `^D`。或者，你也可以輸入 ”exit" 退出命令列。

如果你位於`x終端模擬器`(1)中，你可以使用這個關閉`x終端模擬器`視窗。

### 1.1.8. 怎樣關閉系統

就像任何其他的現代作業系統一樣，Debian會通過記憶體中的快取數據進行文件操作以提高性能，因此在電源被安全地關閉前需要適當的關機過程，通過將記憶體中的數據強制寫入硬盤來維持文件的完整性。如果軟體的電源控制可用，那麼關機過程中會自動關閉系統電源。（否則，你可能需要在關機過程之後按電源鍵幾秒鐘。）

在普通多使用者模式模式下，可以使用命令列關閉系統。

    # shutdown -h now

在單使用者模式下，可以使用命令列關閉系統。

    # poweroff -i -f

參見節 6.3.8, “怎樣通過 SSH 關閉遠端系統”。

### 1.1.9. 恢復一個正常的控制檯

當做了一些滑稽的事（例如“`cat二進位文件`”）後，螢幕會發狂，你可以在命令列輸入“`reset`”。你可能無法在螢幕上看到你輸入的指令。你也可以輸入“`clear`”來清理視窗。

### 1.1.10. 建議新手的額外軟體包

不需任何桌面環境，就能執行Debian的最小安裝方式，但若能使用 `mc` 和 `vim` 併用 `apt-get`(8)，對初學者而言，仍是有用的。

    # apt-get update
     ...
    # apt-get install mc vim sudo aptitude
     ...

如果你已經安裝了這些軟體包，那麼不會有新的軟體包被安裝。

表格 1.1. 有趣的文本模式程序包列表

軟體包 | 流行度 | 大小 | 說明
--- | --- | --- | ---
`mc` | V:43, I:183 | 1590 | 文本模式的全螢幕文件管理器
`sudo` | V:732, I:863 | 6773 | 給普通使用者授予部分 root 權限的程序
`vim` | V:86, I:346 | 4089 | Unix 文本編輯器 Vi 的改進版，一個程式設計師的文本編輯器（標準版）
`vim-tiny` | V:57, I:978 | 1877 | Unix 文本編輯器 Vi 的改進版，一個程式設計師的文本編輯器（精簡版）
`emacs-nox` | V:3, I:13 | 46536 | GNU 項目的 Emacs，基於 Lisp 的擴展文本編輯器
`w3m` | V:11, I:145 | 2853 | 文本模式的 www 瀏覽器
`gpm` | V:9, I:9 | 526 | 文本控制檯 Unix 式樣的貼上拷貝（後台）

閱讀一些資訊文檔，也是一個好的主意。

表格 1.2. 軟體包資訊文檔列表

軟體包 | 流行度 | 大小 | 說明
--- | --- | --- | ---
`doc-debian` | I:882 | 187 | Debian 項目文檔，（Debian 常見問題）和其它文檔
`debian-policy` | I:8 | 5061 | Debian 策略手冊和相關文檔
`developers-reference` | V:0, I:2 | 2601 | Debian 開發者引導方針和資訊
`debmake-doc` | I:0 | 11807 | Debian 維護者手冊
`debian-history` | I:0 | 6251 | Debian 項目歷史
`debian-faq` | I:880 | 791 | Debian 常見問題

你可以用下面的指令安裝這些包。

    # apt-get install package_name

### 1.1.11. 額外使用者帳號

如果你不想用你自己的主使用者帳號來進行下面的練習操作，你可以使用下面的方式創建一個練習使用者帳號，比如說，創建一個使用者名爲 `fish` 的賬號。

    # adduser fish

回答所有問題。

這將創建一個名爲 `fish` 的新帳號。在你練習完成後，你可以使用下面的指令刪除這個使用者帳號和它的使用者主目錄。

    # deluser --remove-home fish

在非 Debian 和特別的 Debian 系統，上面的活動需要使用底層的 `useradd`(8) 和 `userdel`(8)工具代替。

### 1.1.12. sudo 調配

對於典型的單使用者工作站，例如運行在筆記本電腦上的桌面Debian系統，通常簡單地調配`sudo`(8)來使爲非特權使用者（例如使用者`penguin`）只需輸入使用者密碼而非root密碼就能獲得管理員權限。

    # echo "penguin  ALL=(ALL) ALL" >> /etc/sudoers

另外，可以使用下列指令使非特權使用者（例如使用者`penguin`）無需密碼就獲得管理員權限。

    # echo "penguin  ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

這些技巧只對你管理的單使用者工作站中那個唯一的使用者有用。

| 警告
在多使用者工作站中不要建立這樣的普通使用者帳號，因爲它會導致非常嚴重的系統安全問題。

| 注意
在上述例子中，使用者`penguin`的密碼及帳號要有和root帳號密碼同樣多的保護。 在這種情況下，管理員權限被賦予那些有權對工作站進行系統管理任務的人。永遠不要讓你的公司行政管理部門或你的老闆進行管理（例如給予他們權限），除非他們獲得了授權並有這樣的能力。

| 注意
爲了對受限的設備和文件提供存取權限，你應該考慮使用 羣組 提供的近用限制而不是使用 `root` 權限，來自 `sudo`(8)。 隨着越來越細緻周密的調配，`sudo`(8)可以授予一個共享系統上的其它使用者有限的管理權限而不共享root密碼。這可以幫助對有多個管理員的主機進行責任追究，你可以瞭解到是誰做什麼。另一方面，你可能不想任何人有這樣的權限。

### 1.1.13. Play time

現在你已經準備好使用 Debian 系統了，只要你使用非特權使用者帳號就不會有風險。

這是因爲 Debian 系統（即使是預設安裝）會設置適當的文件權限來防止非特權使用者對系統造成破壞。當然，可能仍然有一些漏洞可以利用，但關心這些問題的人不應該閱讀這一節，而應該去閱讀 Debian 安全手冊。

我們使用下面的方式，把 Debian 系統當作一個 類 Unix 系統來學習。

- 節 1.2, “類 Unix 檔案系統” (基本概念)

- 節 1.3, “Midnight Commander (MC)” (生存方式)

- 節 1.4, “類 Unix 工作環境基礎” (基本方式)

- 節 1.5, “簡單 shell 指令” (shell 機制)

- 節 1.6, “類 Unix 的文本處理” (文本處理方式)

## 1.2. 類 Unix 檔案系統

在GNU/Linux和其他類Unix作業系統中，文件被組織到目錄中。所有的文件和目錄排放在以“`/`”爲根的巨大的樹裏。叫它樹是因爲如果你畫出檔案系統，它看起來就像一棵樹，只是它是顛倒過來的。

這些文件和目錄可以分散在多個設備中。`mount`(8)用於把某個設備上找到的檔案系統附着到巨大的文件樹上。相反的，`umount`(8)把它再次分離。在當前的Linux核心裏`mount`(8)帶某些參數，可以把文件樹的一部分綁定到另外的地方，或者可以把檔案系統掛載爲共享的、私有的、從設備、或不可綁定的。對每個檔案系統支援的掛載選項可以在`/usr/share/doc/linux-doc-*/Documentation/filesystems/`找到。

Unix系統上叫做目錄，某些其他系統上叫做文件夾。請同樣留意，在任何Unix系統上，沒有的驅動器的概念，例如“`A:`”。這只有一個檔案系統，並且所有東西都包含在內。這相對於Windows來說是一個巨大的優點。

### 1.2.1. Unix 文件基礎

下面是一些 Unix 文件基礎。

- 文件名是 區分大小寫 的。也就是說， "`MYFILE`" 和 "`MyFile`" 是不同的文件。

- 根目錄意味着檔案系統的根，簡單的稱爲“`/`”，不要把它跟root使用者的家目錄“`/root`”混淆了。

- 每個目錄都有一個名字，它可以包含任意字母或除了`/`”以外的符號。根目錄是個特例。它的名字是“`/`”（稱作“斜線”或“根目錄”），並且它不能被重命名。

- 每個文件或目錄都被指定一個全限定文件名，絕對文件名，或路徑，按順序給出必須經過的目錄從而到達相應目錄。這三個術語是同義的。

- 所有的全限定文件名以“`/`”目錄開始，並且在每個目錄或文件名之間有一個“`/`”。第一個“`/`”是最頂層目錄，其他的“`/`”用於分隔跟着的子目錄。直到到達最後的入口，即實際文件的名稱。這些話可能會令人困惑。用下面這個全限定文件名作爲例子：“`/usr/share/keytables/us.map.gz`”。不過，人們也把它的基名“`us.map.gz`”單獨作爲文件名。

- 根目錄有很多分支，例如“`/etc/`”和“`/usr/`”。這些子目錄依次分出更多的子目錄，例如“`/etc/systemd/`”和“`/usr/local/`”。這整體叫做“目錄樹”。你可以把一個絕對文件名想象成從“`/`”這棵樹的基到某個分支（一個文件）的結尾的一條路徑。你也聽到人們談論目錄樹，就好像它是一個包含所有直系後代的“家庭”樹的一個圖，這個圖叫做根目錄（“`/`”）：因此子目錄有父目錄，並且一條路徑顯示了一個文件完整的祖先。也有相對路徑從其他地方開始，而不是從根目錄。 你應該還記得目錄“`../`”指向父目錄。這個術語也適用於其他類似目錄的結構，如分層數據結構。

- 對於一個實體設備, 是沒有一個特定的目錄路徑名來對應的組成部分. 這不同於RT-11, CP/M,OpenVMS,MS-DOS,AmigaOS, 以及微軟的Windows，這些系統存在一個路徑包含了一個設備名字，比如"`C:\`"。(儘管如此, 路徑條目確實存在引用了物理設備作爲正常的檔案系統的一部分. 參考節 1.2.2, “檔案系統深入解析”。)

| 注意
雖然你可以在文件名中使用任意的字幕或者符號, 但是在實際情況下這樣做是一個壞主意. 最好避免使用一些在命令列裏面含有特殊意義的字元, 比如空格, 製表符, 換行符, 和其它的特殊字元: `{ } ( ) [ ] ' ` " \ / >< | ; ! #&^ * % @ $`. 如果你想有一個區分度良好的命名, 比較好的選擇是利用 時期, 連字元和下劃線. 你也可以每個單詞的首字母大寫, 這叫大駝峯命名法, 比如這樣 "`LikeThis`". 經驗豐富的Linux使用者會趨向於在文件名中不使用空格.

| 注意
這個 "root" 可能既表示 "超級使用者root" 又表示 " 根目錄"(/root) . 應該根據上下文確定它的用法.

| 注意
單詞path不僅表示包含全限定文件名, 也可能表示指令搜索的路徑. 通常路徑真實的意思是需要通過上下文來明確.

關於檔案階層的最佳詳細實踐在檔案系統階層標準("`/usr/share/doc/debian-policy/fhs/fhs-2.3.txt.gz`" 和 `hier` (7)). 你應該記住以下的一些標準作爲開始學習的步驟.

表格 1.3. 重要目錄的用途列表

目錄 | 目錄用途
--- | ---
`/` | 根目錄
`/etc/` | 系統範圍的調配文件
`/var/log/` | 系統日誌文件
`/home/` | 所有非特權使用者的使用者目錄

### 1.2.2. 檔案系統深入解析

按照UNIX系统的傳統，Debian GNU / Linux 的文件系统是在硬體數據儲存設備，諸如硬碟或其他的儲存設備上，與硬體的互動，如控制台和遠端序列終端都是以統一的方式呈現在 “`/ dev /`” 下面。

每個檔案、目錄、命名管道（一種兩個程式間共享資料的方法）或 Debian GNU/Linux 系統上的物理裝置都有一個叫做 inode的資料結構，描述了其相關特性，例如擁有它的使用者（所有者），它屬於的群組，最後一次存取時間，等等。把所有東西都表示在檔案系統中的想法是來源於 Unix，現代的 Linux 核心則將這個思路進行了擴充。現在，甚至有關計算機上正在執行的程序的資訊都可以在檔案系統中找到。

這個對物理實體和內部行程的統一和抽象是非常強大的，因爲這允許我們用同樣的指令對許多完全不同的設備進行同樣的操作。甚至可以通過向鏈接到運行行程的特殊文件寫入數據來改變核心的運行方式。

| 提示
如果你需要識別文件樹和物理實體之間的對應關係，不帶參數運行`mount`(8)。

### 1.2.3. 檔案系統權限

類Unix系統的檔案系統權限被定義給三類受影響的使用者。

- 擁有這個文件的使用者（u）

- 這個文件所屬組的其他使用者（g）

- 所有其餘的使用者（o），同樣稱爲“世界”和“所有人”

對文件來說，每個對應權限允許下列動作。

- 可讀（r）權限允許所有者檢查文件的內容。

- 可寫（w）權限允許所有者修改文件內容。

- 可執行（x）權限允許所有者把文件當做一個指令運行。

對於目錄來說，每個對應權限允許下列動作。

- 可讀（r）權限允許所有者列出目錄內的內容。

- 可寫（w）權限允許所有者添加或刪除目錄裏面的文件。

- 可執行（x）權限允許所有者存取目錄裏的文件。

在這裏，一個目錄的可執行權限意味着不僅允許讀目錄裏的文件，還允許顯示他們的屬性，例如大小和修改時間。

`ls`(1)用於顯示文件和目錄的權限資訊（更多）。當運行時帶有“`-l`”選項，它將按給定順序顯示下列資訊。

- 文件類型（第一個字母）

- 文件的存取權限（9個字元，三個字元組成一組按照使用者、組、其他的順序表示）

- 鏈接到文件的硬鏈接數

- 文件所有者的使用者名

- 這個文件所屬的組名

- 以字元（字節）爲單位的文件大小

- 文件的日期和時間（mtime）

- 文件的名字

表格 1.4. “`ls -l`”輸出的第一個字元列表

字元 | 說明
--- | ---
`-` | 普通文件
`d` | 目錄
`l` | 符號鏈接
`c` | 字元裝置節點
`b` | 塊裝置節點
`p` | 命名管道
`s` | 套接字

`chown`(1)用於 root 帳號修改文件的所有者。`chgrp`(1)用於文件的所有者或 root 帳號修改文件所屬的組。`chmod`(1)用於文件的所有者或root帳號修改文件和文件夾的存取權限。操作一個`foo`文件的基本語法如下 。

    # chown newowner foo
    # chgrp newgroup foo
    # chmod  [ugoa][+-=][rwxXst][,...] foo

例如，你可以按照下面使一個目錄樹被使用者`foo`所有，並共享給組`bar`。

    # cd /some/location/
    # chown -R foo:bar .
    # chmod -R ug+rwX,o=rX .

有三個更加特殊的權限位。

- Set_User_ID位（s或S替換使用者的x）

- Set_Group_ID位（s或S替換組的x）

- 粘置位（t或T替代其他使用者的x）

這裏“`ls -l`”對這些位的輸出是大寫的，如果這些輸出裏隱藏了可執行位，則它未設置。

給一個可執行文件設置Set-User-ID位將允許一個使用者以他自己的ID運行這個可執行文件（例如root）。類似的，給一個可執行文件設置了Set-Group-ID位將允許一個使用者以文件的組ID運行該文件。（例如root組）。由於這些設置可能引起安全風險，使能它們的時候需要格外留意。

在一個目錄上設置“Set-Group-ID”將打開類 BSD 的文件創建計劃，所有在目錄裏面創建的文件將屬於目錄所屬的組。

給一個目錄設定“粘滯位”將保護該目錄內的檔案不被其所有者之外的一個使用者刪除。為了保護一個在像“`/tmp`”這樣所有人可寫或同組可寫的目錄下檔案內容的安全，不僅要去除可寫許可權，還要給其所在目錄設定粘滯位。否則，該檔案可以被任意對其所在目錄有寫許可權的使用者刪除並建立一個同名的新檔案。

這裏有少量有趣的文件權限例子。

    $ ls -l /etc/passwd /etc/shadow /dev/ppp /usr/sbin/exim4
    crw------T 1 root root   108, 0 Oct 16 20:57 /dev/ppp
    -rw-r--r-- 1 root root     2761 Aug 30 10:38 /etc/passwd
    -rw-r----- 1 root shadow   1695 Aug 30 10:38 /etc/shadow
    -rwsr-xr-x 1 root root   973824 Sep 23 20:04 /usr/sbin/exim4
    $ ls -ld /tmp /var/tmp /usr/local /var/mail /usr/src
    drwxrwxrwt 14 root root  20480 Oct 16 21:25 /tmp
    drwxrwsr-x 10 root staff  4096 Sep 29 22:50 /usr/local
    drwxr-xr-x 10 root root   4096 Oct 11 00:28 /usr/src
    drwxrwsr-x  2 root mail   4096 Oct 15 21:40 /var/mail
    drwxrwxrwt  3 root root   4096 Oct 16 21:20 /var/tmp

`chmod`(1)有另一種數值模式來描述檔案許可權。這種數字模式使用3到4位八進位制（底為8）數。

表格 1.5. `chmod`(1) 指令文件權限的數字模式

數字 | 說明
--- | ---
第一個可選數字 | set user ID (=4), set group ID (=2) 和 sticky bit (=1) 之和
第二個數字 | read (=4), write (=2), 和 execute (=1) 權限之和， user 使用者
第三個數字 | 同上， group
第四個數字位 | 同上， other

這聽起來很複雜實際上相當簡單。如果你把“`ls -l`”指令輸出的前幾列（2-10），看成以二進位制（底為2）表示檔案的許可權（“-”看成0，“rwx”看成1），你應該可以理解用數字模式值的最後3位數字對檔案許可權的八進位制表示。

嘗試下列例子

    $ touch foo bar
    $ chmod u=rw,go=r foo
    $ chmod 644 bar
    $ ls -l foo bar
    -rw-r--r-- 1 penguin penguin 0 Oct 16 21:39 bar
    -rw-r--r-- 1 penguin penguin 0 Oct 16 21:35 foo

| 提示
如果你需要在 shell 指令碼中存取“`ls -l`”顯示的資訊，你需要使用相關指令，如`test`(1)，`stat`(1)和`readlink`(1)。shell 內建指令，如“`[`”或“`test`”，可能也會用到。

### 1.2.4. 控制新建檔案的許可權：umask

什麼許可權將應用到新建檔案受 shell 內建指令 `umask` 的限制。參見`dash`(1)，`bash`(1)，和`內建命令`(7)。

     (file permissions) = (requested file permissions) & ~(umask value)

表格 1.6. umask值舉例

umask值 | 建立的檔案許可權 | 建立的目錄許可權 | 用法
--- | --- | --- | ---
`0022` | `-rw-r--r--` | `-rwxr-xr-x` | 僅所屬使用者可寫
`0002` | `-rw-rw-r--` | `-rwxrwxr-x` | 僅所屬組可寫

Debian 預設使用使用者私人組（UPG）。每當一個新使用者新增到系統的時候都會建立一個UPG。UPG 的名字和建立它的使用者相同，這個使用者是這個UPG的唯一成員。自從每個使用者都有自己的私人組之後，把umask設定成`0002`變得更安全了。（在某些 Unix 變體中，把所有普通使用者設定到一個叫`users`的組是非常常見的做法，在這種情況下，出於安全考慮把umask設為`0022`是一個好主意）

| 提示
通過把 “`umask 002`” 寫入 `~/.bashrc` 檔案開啟UPG。

### 1.2.5. 一組使用者的許可權（組）

| 警告
在做重啟或者類似行為前，確保儲存沒有儲存的修改。

你能夠增加一個使用者 `penguin` 到 `bird` 組，在兩個步驟內：

- 使用下面中的一個改變組配置：

  - 執行 "`sudo usermod -aG bird penguin`"。

  - 執行"`sudo adduser penguin bird`"。（只在典型的 Debian 系統）

  - 執行"`sudo vigr`"編輯 `/etc/group` 和"`sudo vigr -s`"編輯 `/etc/gshadow` 來追加 `penguin` 到`bird` 行。

- 使用下面中的一個來應用配置：

  - 冷重啟再登入。（最佳選項）

  - 透過 GUI（圖形使用者介面）選單登入和登出（這可能無法在現代桌面環境下工作。）

你能夠從 `bird` 組移除使用者 `penguin`，用兩個步驟：

- 使用下面中的一個改變組配置：

  - 執行 "`sudo usermod -rG bird penguin`"。

  - 執行 "`sudo deluser penguin bird`"。（只在典型的 Debian 系統）

  - 執行"`sudo vigr`"編輯 `/etc/group` 和"`sudo vigr -s`"編輯 `/etc/gshadow` 來移除 `bird` 行裡面的`penguin`。

- 使用下面中的一個來應用配置：

  - 冷重啟再登入。（最佳選項）

  - 執行 "`kill -TERM -1`" 並做一些修復行為，比如"`systemctl restart NetworkManager.service`"。

  - 透過 GUI（圖形使用者介面）選單登出，不是 Gnome 桌面的一個選項。

在現代桌面系統，任何熱重啟嘗試是真實的冷重啟的脆弱替代。

| 注意
或者，你可以通過新增一行 “`auth optional pam_group.so`”到 “`/etc/pam.d/common-auth`” 以及調配 “`/etc/security/group.conf`” ，使得在身份驗證過程動態新增使用者到組。（參見章 4, 認證和訪問控制。）

The hardware devices are just another kind of file on the Debian system. If you have problems accessing devices such as USB flash drive and CD-ROM from a user account, you should make that user a member of the relevant group.

一些著名的由系統提供的組允許其成員不需要 `root` 許可權存取某些特定的檔案和裝置。

表格 1.7. 關於檔案存取的由系統提供的著名組列表

組 | 可存取檔案和裝置的描述
--- | ---
`dialout` | 完全及直接的存取串列埠埠（“`/dev/ttyS[0-3]`”）
`dip` | 有限的存取串列埠，建立到信任點的撥號 IP 連線
`cdrom` | CD-ROM, DVD+/-RW 驅動器
`audio` | 音訊裝置
`video` | 視訊裝置
`scanner` | 掃描器
`adm` | 系統監控日誌
`staff` | 一些用於初級管理工作的目錄：“`/usr/local`”，“`/home`”

| 提示
你需要屬於 `dialout` 組使得可以重調配調變解調器，撥號到任意地方，等等。但如果`root` 使用者在 “`/etc/ppp/peers/`” 為受信任點建立了預定義組態檔案的話，你只需要屬於`dip` 組，就可以通過使用`pppd`(8)、`pon`(1)，以及`poff`(1)指令 建立撥號 IP 連結到這些受信任的點。

某些著名的由系統提供的組允許它們的成員不帶 `root` 許可權執行特定的指令。

表格 1.8. 著名的由系統提供用於特定指令執行的組列表

組 | 可存取指令
--- | ---
`sudo` | 使用超級使用者許可權執行任何命令
`lpadmin` | 執行指令以從印表機資料庫新增、修改、移除印表機

由系統提供的使用者和組的完整列表，參見由 `base-passwd`包提供的“`/usr/share/doc/base-passwd/users-and-groups.html`”中，當前版本的“使用者和組”。

使用者和組系統的管理指令，參見`passwd`(5)，`group`(5)，`shadow`(5)，`newgrp`(1)，`vipw`(8)，`vigr`(8)，以及`pam_group`(8)。

### 1.2.6. 時間戳

GNU/Linux 文件有三種類型的時間戳。

表格 1.9. 時間戳類型列表

類型 | 含義（歷史上 Unix 的定義）
--- | ---
mtime | 文件修改時間(`ls-1`)
ctime | 文件狀態修改時間 (`ls -lc`)
atime | 文件最後被存取的時間 (`ls -lu`)

| 注意
ctime 不是文件創建時間。

| 注意
atime在 GNU/Linux 系統上的真實值可能和歷史上 Unix 的定義有所不同。

- 覆蓋一個文件，將會改變該文件所有的 mtime, ctime, 和 atime 屬性。

- 改變文件的所有者或者權限，將改變文件的 ctime 和 atime 屬性。

- 在歷史上的 Unix 系統中，讀取一個檔案將改變檔案的 atime 屬性。

- 讀一個檔案，將改變檔案的 atime屬性；在 GNU/Linux 系統上，這僅發生在其檔案系統使用“`strictatime`”引數掛載的情況下。

- 如果 GNU/Linux 系統的檔案系統使用 "`relatime`" 選項掛載，第一次讀檔案，或者隨後讀檔案，將改變該檔案的 atime 屬性. (從 Linux 2.6.30 開始的預設行為)

- 如果 GNU/Linux 系統的檔案系統使用 "`noatime`" 掛載，則讀一個檔案，不會改變這個檔案的 atime 屬性。

| 注意
為了在正常的使用場景中能夠提升檔案系統的讀取效率，新增了 "`noatime`" 和 "`relatime`" 這兩個載入選項。如使用了 "`strictatime`" 選項，即使簡單的檔案讀操作都伴隨著更新 atime 屬性這個耗時的寫操作。但是 atime 屬性除了 `mbox`(5) 檔案以外卻很少用到。詳情請看 `mount`(8)。

使用 `touch`(1) 指令修改已存在檔案的時間戳。

對於時間戳，在非英語區域（“`fr_FR.UTF-8`”），`ls` 命令輸出本地化字串。

    $ LANG=C  ls -l foo
    -rw-rw-r-- 1 penguin penguin 0 Oct 16 21:35 foo
    $ LANG=en_US.UTF-8  ls -l foo
    -rw-rw-r-- 1 penguin penguin 0 Oct 16 21:35 foo
    $ LANG=fr_FR.UTF-8  ls -l foo
    -rw-rw-r-- 1 penguin penguin 0 oct. 16 21:35 foo

| 提示
參考節 9.3.4, “定製時間和日期的顯示” 自定義 “`ls -l`” 輸出 。

### 1.2.7. 連結

有兩種方法把一個檔案 “`foo`” 連結到一個不同的檔名 “`bar`”。

- 硬連結

  - 對現有檔案重複名稱

  - “`ln foo bar`”

- 符號連結或 symlink

  - 通過名字指向另一個檔案的特殊檔案

  - “`ln -s foo bar`”

請參閱下面的示例，`rm` 指令結果中連結數的變化和細微的差別。

    $ umask 002
    $ echo "Original Content" > foo
    $ ls -li foo
    1449840 -rw-rw-r-- 1 penguin penguin 17 Oct 16 21:42 foo
    $ ln foo bar     # hard link
    $ ln -s foo baz  # symlink
    $ ls -li foo bar baz
    1449840 -rw-rw-r-- 2 penguin penguin 17 Oct 16 21:42 bar
    1450180 lrwxrwxrwx 1 penguin penguin  3 Oct 16 21:47 baz -> foo
    1449840 -rw-rw-r-- 2 penguin penguin 17 Oct 16 21:42 foo
    $ rm foo
    $ echo "New Content" > foo
    $ ls -li foo bar baz
    1449840 -rw-rw-r-- 1 penguin penguin 17 Oct 16 21:42 bar
    1450180 lrwxrwxrwx 1 penguin penguin  3 Oct 16 21:47 baz -> foo
    1450183 -rw-rw-r-- 1 penguin penguin 12 Oct 16 21:48 foo
    $ cat bar
    Original Content
    $ cat baz
    New Content

硬連結可以在同一個檔案系統內建立，並共用同一個 inode 號，由`ls`(1)帶 “`-i`”選項顯示。

符號連結總是名義上具有“`rwxrwxrwx`”的檔案存取許可權，如上面例子所示，實際的有效存取許可權由它所指向的檔案確定。

| 注意
除非你有非常好的理由，否則不要建立一個複雜的符號連結或硬連結通常是個好主意。符號連結的邏輯組合可能導致檔案系統噩夢般的無限迴圈。

| 注意
通常使用符號連結比使用硬連結更合適，除非你有一個好理由使用硬連結。

“`.`”目錄連結到它所在的目錄，因此任何新建目錄的連結數從2開始。“`..`”目錄連結到父目錄，因此目錄的連結數隨著新的子目錄的建立而增加。

如果你剛從Windows遷移到Linux，你很快將清楚 Unix 的檔名連結相較於Windows最相近的“快捷方式”是多麼精心設計的。由於它是在檔案系統中實現的，應用無法看到連結檔案跟原始檔案之間的區別。在硬連結這種情況，這真的是毫無差別。

### 1.2.8. 命名管道（先進先出）

命名管道是一個像管道一樣的檔案。你把內容放進了檔案，它從另一端出來。因此，它被稱為FIFO，即先進先出：你從管道這端先放進去的東西會從另一端先出來。

如果對一個命名管道進行寫入操作，寫入的過程不會被終止，直到寫入的資訊從管道中被讀取出來。讀取過程將會持續到沒有資訊可以讀取為止。管道的大小始終是零，它不儲存資料，它只是連線兩個過程，像shell提供的 "`1|` 2" 語法功能一樣。然而，一旦管道有了名稱，這兩個程序就可以不必在同一個指令列，甚至由同一個使用者執行。管道是 UNIX 的一個非常有影響力的創新。

嘗試下列例子

    $ cd; mkfifo mypipe
    $ echo "hello" >mypipe & # put into background
    [1] 8022
    $ ls -l mypipe
    prw-rw-r-- 1 penguin penguin 0 Oct 16 21:49 mypipe
    $ cat mypipe
    hello
    [1]+  Done                    echo "hello" >mypipe
    $ ls mypipe
    mypipe
    $ rm mypipe

### 1.2.9. 套接字

套接字被廣泛應用於所有的網際網路通訊，資料庫和作業系統本身。它類似於命名管道（FIFO）並且允許程序之間甚至不同計算機之間進行資訊交換。對於套接字，這些程序不需要在同一時間執行，也不需要是同一個父程序的子程序。它是程序間通訊（IPC）的一個節點。資訊的交換可能會通過網路發生在不同主機之間。最常見的兩種是 網際網路套接字 和 UNIX域套接字 。

| 提示
通過 "`netstat -an`" 指令可以很方便的檢視系統已經打開了那些套接字。

### 1.2.10. 設備文件

設備文件包括系統的物理設備和虛擬設備，如硬盤、顯卡、顯示屏、鍵盤。虛擬設備的一個例子是控制檯，用“`/dev/console`”來描述。

設備文件有兩種類型。

- 字元設備

  - 每次存取一個字元

  - 一個字元等於一個字節

  - 如鍵盤、串口…

- 塊設備

  - 通過更大的單元–塊，進行存取

  - 一個塊>一個字節

  - 如硬盤等…

你可以讀寫塊設備文件，儘管該文件可能包含二進位數據，讀取後顯示出無法理解的亂碼。向文件寫入數據，有時可以幫助定位硬體連接故障。比如，你可以將文本文件導入打字機設備“`/dev/lp0`”，或者將調製解調指令發送到合適的串口“`/dev/ttyS0`”。但是，除非這些操作都小心完成，否則可能會導致一場大災難。所以要特別小心。

| 注意
常規存取打字機，使用`lp`(1)。

設備的節點數可以通過執行`ls`(1)得到，如下所示。

    $ ls -l /dev/sda /dev/sr0 /dev/ttyS0 /dev/zero
    brw-rw---T  1 root disk     8,  0 Oct 16 20:57 /dev/sda
    brw-rw---T+ 1 root cdrom   11,  0 Oct 16 21:53 /dev/sr0
    crw-rw---T  1 root dialout  4, 64 Oct 16 20:57 /dev/ttyS0
    crw-rw-rw-  1 root root     1,  5 Oct 16 20:57 /dev/zero

- "`/dev/sda`"的主設備號是8，次設備號是0。它可以被`disk`羣組的使用者讀寫。

- "`/dev/sr0`"的主設備號是11，次設備號是0。它可以被`cdrom`羣組的使用者讀寫。

- "`/dev/ttyS0`"的主設備號是4，次設備號是64。它可以被`dailout`羣組的使用者讀寫。

- "`/dev/zero`"的主設備號是1，次設備號是5。它可以被任意使用者讀寫。

在現代Linux系統中，處在"`/dev`"之下的檔案系統會自動被`udev`()機制填充。

### 1.2.11. 特別設備文件

還有一些特別的設備文件。

表格 1.10. 特別設備文件列表

設備文件 | 操作 | 響應描述
--- | --- | ---
`/dev/null` | 讀取 | 回傳“文件結尾字元（EOF）“
`/dev/null` | 寫入 | 無回傳（一個無底的數據轉存深淵）
`/dev/zero` | 讀取 | 回傳"`\0`空字元"（與ASCII中的數字0不同）
`/dev/random` | 讀取 | 從真隨機數產生器回傳隨機字元，提供真熵（緩慢）
`/dev/urandom` | 讀取 | 從能夠安全加密的僞隨機數產生器回傳隨機字元
`/dev/full` | 寫入 | 回傳磁盤已滿（ENOSPC）錯誤

這些特別設備文件經常和shell數據重導向聯合使用（參考節 1.5.8, “典型的順序指令和 shell 重導向”）。

### 1.2.12. procfs 和 sysfs

procfs和sysfs兩個僞檔案系統，分別掛載於"`/proc`"和"`/sys`"之上，將核心中的數據結構暴露給使用者空間。或者說，這些條目是虛擬的，他們打開了深入瞭解作業系統運行的方便之門。

目錄"`/proc`"爲每個正在運行的行程提供了一個子目錄，目錄的名字就是行程的 PID。需要讀取行程資訊的系統工具，如`ps`()，可以從這個目錄結構獲得資訊。

"`/proc/sys`"之下的目錄，包含了可以更改某些核心運行參數的接口。（你也可以使用專門的`sysctl`()指令修改，或者使用其預加載/調配文件"`/etc/sysctl.conf`"。）

當人們看到這個特別大的文件"`/proc/kcore`"時，常常會驚慌失措。這個文件於你的的電腦記憶體大小相差不多。它被用來調試核心。它是一個虛擬文件，指向系統記憶體，所以不必擔心它的大小。

"`/sys`"以下的目錄包含了核心輸出的數據結構，它們的屬性，以及它們之間的鏈接。它同時也包含了改變某些核心運行時參數的界面。

參考"`proc.txt(.gz)`"，"`sysfs.txt(.gz)`"，以及其他相關的Linux核心文檔（"`/usr/share/doc/linux-doc-*/Documentation/filesystems/*`"），這些文件由`linux-doc-*`軟體包提供。

### 1.2.13. tmpfs

tmpfs是一個臨時檔案系統，它的文件都保存在虛擬記憶體中。必要時，位於記憶體頁快取的tmpfs數據可能被交換到硬碟中的交換分區。

系統啓動早期階段，"`/run`"目錄掛載爲tmpfs。這樣即使"`/`"掛載爲只讀，它也是可以被寫入的。它爲過渡態文件提供了新的存儲空間，同時也替代了Filesystem Hierarchy Standar2.3版中說明的目錄位置：

- "`/var/run`" → "`/run`"

- "`/var/lock`" → "`/run/lock`"

- "`/dev/shm`" → "`/run/shm`"

參考"`tmpfs.txt(.gz)`", 文件位於Linux核心文檔（"`/usr/share/doc/linux-doc-*/Documentation/filesystems/*`"）目錄之下，由軟體包`linux-doc-*`提供。

## 1.3. Midnight Commander (MC)

Midnight Commander (MC) 是一個Linux終端或其它終端環境下的 GNU 版 "瑞士刀" 。它為新手們提供了一個選單式樣的終端使用體驗，這更易於學習運用標準的 Unix 指令。

你可能需要按照下面的指令來安裝標題為 "`mc` " 的Midnight Commander 包.

    $ sudo apt-get install mc

使用 `mc`(1) 指令那個來瀏覽 Debian 系統。這是最好的學習方式。請使用游標鍵和Enter 鍵來翻看一些感興趣的內容。

- "`/etc`" 及其子目錄

- "`/var/log` " 及其子目錄

- "`/usr/share/doc` " 及其子目錄

- "`/usr/sbin` " 和 "`/usr/bin` "

### 1.3.1. 自定義 MC

為了在退出 MC 的時候更改目錄並 `cd` 到其它目錄，我建議修改 "`~/.bashrc`" 包含一個由 `mc` 包提供的指令碼。

    . /usr/lib/mc/mc.sh

檢視`mc`(1) (在 "`-P`" 選項裡) 的原因。 (如果你不能理解我這裡說所講的，你可以稍後回頭再看)

### 1.3.2. 啟動 MC

MC 可以這樣啟動起來。

    $ mc

MC 通過選單覆蓋了所有的檔案操作，因此而讓使用者更省心省力。只需要按 F1 就可以跳轉到幫助介面。你只需要按游標鍵和功能鍵就可以使用 MC。

| 注意
某些終端比如 `gnome-terminal`(1)，功能鍵的按鍵觸發訊息可能會被終端程式擷取。在 `gnome-terminal` 裡，可以透過 "首選項" →“通用”-> "快捷鍵" 選單設定來停用這些特徵。

如果你遇到字元編碼問題，顯示出來都是亂碼，通過新增"`-a`"到 MC 指令列或許有助於避免此類問題。

如果這樣不能解決 MC 中的顯示問題，可以參考 節 9.5.6, “終端調配”.

### 1.3.3. MC 文件管理

預設的兩個目錄面板裡包含了檔案列表。另一個有用的模式是設定右邊視窗為 "資訊" 來讀取檔案存取許可權資訊。接下來是一些必要的快捷鍵。背景程式`gpm`(8)執行的時候，你也可以在字元指令列裡用滑鼠來操作。 (在 MC 裡進行復制和貼上操作的時候一定要按住 shift 鍵。)

表格 1.11. MC 快捷鍵綁定

快捷鍵 | 鍵綁定功能
--- | ---
`F1` | 幫助清單
`F3` | 內部檔案檢視器
`F4` | 內部編輯器
`F9` | 啟用下拉選單
`F10` | 退出 Midnight Commander
`Tab` | 在兩個視窗間移動
`Insert` 或 `Ctrl-T` | 用於多檔案操作的標記檔案，如副本
`Del` | 刪除檔案 (注意---設定 MC 為安全刪除模式)
游標鍵 | 自我解釋

### 1.3.4. MC 指令列技巧

- `cd` 指令在選中的螢幕中改變目錄。

- `Ctrl-Enter` or `Alt-Enter` 拷貝檔名到指令列。使用 `cp`(1) 和 `mv`(1) 兩個指令來進行處理。

- `Alt-Tab` 顯示檔名自動補全提示。

- 通過新增 MC 指令參數可以指定開始目錄；例如，"`mc /etc /root`"。

- `Esc` + `n-key` → `Fn` (i.e., `Esc` + `1` → `F1`, etc.; `Esc` + `0` → `F10`)

- 先按 `Esc` 鍵 和同時按 `Alt` 是一樣；例如, 輸入 `Esc` + `c` 和同時 `Alt-C`是一樣的。`Esc` 被稱為 meta 鍵，有時候也稱之為 "`M-`"。

### 1.3.5. MC 內部編輯器

這個內建編輯器有一個有意思的貼上方案。摁`F3` 開始選擇起始點，再摁`F3` 選擇終點並高亮選擇區。此刻你可以移動你的游標，使用 F6 將選區移動到當前游標下，F5 則將選區複製到當前游標下。`F2` 儲存檔案。`F10` 退出。多數游標鍵以直觀的方式工作。

MC 編輯器可以直接以下面的指令方式啟動。

    $ mc -e filename_to_edit

    $ mcedit filename_to_edit

這不是一個多視窗編輯器，但是能通過複用終端來達到同樣的效果。在兩個視窗間複製，需要用到 Alt-F < n > 來切換虛擬終端並使用 "File→Insert file" 或者 "File→Copy to file" 來移動文字。

內部編輯器可以被外部編輯器替代。

同樣，許多程式使用環境變數`$EDITOR`或`$VISUAL`來決定編輯器的使用。如果你準備使用`vim`(1)或者`nano`(1)來開始，你或許需要將下面的程式碼加入"`~/.bashrc`"來對`mcedit`進行設定。

    export EDITOR=mcedit
    export VISUAL=mcedit

如果可能的話我推薦用 "`vim`"。

如果你使用`vim`(1)並不順手，你可以在大部分系統中繼續使用`mcedit`(1)來進行工作。

### 1.3.6. MC 內部檢視器

MC是一個非常智慧的檢視器。這是一個在文件中搜索文字的好工具。我經常使用它在`/usr/share/doc`目錄中查詢檔案。這是瀏覽大量Linux資訊的最快方式。這個檢視器可以通過下列指令中的任何一個來直接啟動。

    $ mc -v path/to/filename_to_view

    $ mcview path/to/filename_to_view

### 1.3.7. 自動啟動 MC

在檔案中輸入回車， 用適當的程式來處理檔案的內容 (檢視 節 9.4.11, “自定義被啟動的程式”)。這是 MC 一個非常方便的用法。

表格 1.12. MC 中對Enter 鍵的響應

檔案型別 | 對Enter 鍵的響應
--- | ---
可執行檔案 | 執行指令
幫助文件 | 管道內容檢視器軟體
html 檔案 | 管道內容網頁瀏覽器
"`*.tar.gz`" 和 "`*.deb`" 文件 | 瀏覽其內容就像檢視子目錄一樣

為讓這些檢視器和虛擬檔案特徵生效，可檢視的檔案不能夠被設定為可執行。使用 `chmod`(1) 或通過 MC 檔案選單改變他們的狀態。

### 1.3.8. MC 中的 虛擬檔案系統

MC 能夠跨因特網訪問檔案。在選單按 `F9`, "`Enter`" 和"`h`"來啟用 Shell 檔案系統。按 "`sh://[user@]machine[:options]/[remote-dir]`" 的形式輸入 URL,就會看起來像本地使用 `ssh` 一樣來檢索遠端目錄。

## 1.4. 類 Unix 工作環境基礎

雖然 MC 差不多可以讓你做任何事情，但學會從 shell 提示下使用指令列工具也是非常重要的，可以讓你變得熟悉類 Unix 工作環境。

### 1.4.1. 登入 shell

因登入 shell 可以被一些系統初始化程式使用，請謹慎的把登入 shell 保持為 `bash`(1) ，並避免把它轉換為 `chsh`(1)。

如果你想使用不同的 shell 互動提示符，從 GUI（圖形使用者介面）的終端模擬器來設定；或者從 `~/.bashrc` 啟動，比如說，在裡面放置"`exec /usr/bin/zsh -i -l`" 或 "`exec /usr/bin/fish -i -l`"。

表格 1.13. shell 程式列表

軟體包 | 流行度 | 大小 | POSIX shell | 說明
--- | --- | --- | --- | ---
`bash` | V:866, I:999 | 7277 | Yes | Bash: GNU Bourne Again SHell (事實上的標準)
`bash-completion` | V:35, I:953 | 1952 | N/A | bash shell 程式設計補全
`dash` | V:906, I:998 | 207 | Yes | Debian Almquist Shell, 擅長 shell 指令碼
`zsh` | V:40, I:70 | 2509 | Yes | Z shell:有許多增強的標準 shell
`tcsh` | V:4, I:15 | 1366 | No | TENEX C Shell: 一個 Berkeley csh 的增強版本
`mksh` | V:5, I:8 | 7713 | Yes | Korn shell 的一個版本
`csh` | V:1, I:5 | 348 | No | OpenBSD C Shell, Berkeley csh 的一個版本
`sash` | V:0, I:5 | 1335 | Yes | 有內建指令的 Stand-alone shell (並不意味著標準的 "`/usr/bin/sh`")
`ksh` | I:8 | 65 | Yes | Korn shell的真正的 AT&T 版本
`rc` | V:0, I:0 | 182 | No | AT&T Plan 9 rc shell 的一個實現
`posh` | V:0, I:0 | 187 | Yes | Policy-compliant Ordinary SHell 策略相容的普通 shell(`pdksh` 衍生實現)

| 提示
雖然類 POSIX 共享基本語法，但他們在 shell 變數和 glob 擴充等基本事情上，行為可以不同。細節請查閱他們的文件。

在本教學中，互動式的 shell 總是指 `bash`.

### 1.4.2. 定製bash

您可客製化 `bash`(1) 行為，使用 "`~/.bashrc`"。

嘗試下列例子。

    # enable bash-completion
    if ! shopt -oq posix; then
      if [ -f /usr/share/bash-completion/bash_completion ]; then
        . /usr/share/bash-completion/bash_completion
      elif [ -f /etc/bash_completion ]; then
        . /etc/bash_completion
      fi
    fi

    # CD upon exiting MC
    . /usr/lib/mc/mc.sh

    # set CDPATH to a good one
    CDPATH=.:/usr/share/doc:~:~/Desktop:~
    export CDPATH

    PATH="${PATH+$PATH:}/usr/sbin:/sbin"
    # set PATH so it includes user's private bin if it exists
    if [ -d ~/bin ] ; then
      PATH="~/bin${PATH+:$PATH}"
    fi
    export PATH

    EDITOR=vim
    export EDITOR

| 提示
可以找到其他 `bash` 客製化技巧，諸如 節 9.3.6, “有顏色輸出的指令”，在 章 9, 系統技巧之內。

| 提示
`bash-completion` 軟體包能夠讓 `bash` 進行指令補全.

### 1.4.3. 特殊按鍵

在類Unix環境，有一些具有特殊含義的按鍵。請注意，普通的Linux字元控制台，只有左手邊的`Ctrl`和`Alt`鍵可以正常運作。其中有幾個值得記住的按鍵。

表格 1.14. bash的按鍵綁定列表

快捷鍵 | 描述 key binding
--- | ---
`Ctrl-U` | 刪除光標前到行首的字元
`Ctrl-H` | 刪除光標前的一個字元
`Ctrl-D` | 終止輸入（如果你在使用shell，則退出shell）
`Ctrl-C` | 終止一個正在執行的程式
`Ctrl-Z` | 通過將程序移動到後臺來暫停程序
`Ctrl-S` | 停止螢幕輸出
`Ctrl-Q` | 重啟螢幕輸出
`Ctrl-Alt-Del` | 重啓/關閉系統，參見`inittab`(5)
`Left-Alt-key`（或`Windows-key`） | Emacs和相似UI的元鍵（meta-key）
`Up-arrow` | 開始在`bash`中的指令歷史搜索
`Ctrl-R` | 開始在`bash`中的增量指令歷史搜索
`Tab` | 在 `bash` 命令列中補全文件名
`Ctrl-V` `Tab` | 在 `bash` 命令列中輸出 `Tab` 而不是進行補全

| 提示
`Ctrl-S`的終端功能可能被`stty`(1)禁用。

### 1.4.4. 滑鼠操作

Debian 系統針對文字的滑鼠操作混合 2 種風格，外加一些新的方法：

- 傳統的 Unix 滑鼠操作方式：

  - 使用 3 個按鈕（單擊）

  - 使用主要鍵

  - 使用圖形化應用程式像是 xterm 和 text 應用程式於 Linux console

- 現代 GUI（圖形使用者介面）滑鼠操作方式：

  - 拖曳並點擊

  - 使用 PRIMARY 和剪貼簿

  - 使用於現代 GUI 應用程式像是 `gnome-terminal`

表格 1.15. 游標右鍵操作及相關按鍵操作列表於 Debian

操作 | 回應
--- | ---
左擊並拖動滑鼠游標 | 主要鍵的選擇作為選擇範圍
單擊左鍵 | 主要鍵的位置作為選擇範圍的開頭
單擊右鍵（傳統方式） | 主要鍵的位置作為選擇範圍的結尾
單擊右鍵（現代方式） | 前後文相依清單 (cut/copy/paste)
點選中鍵或者 Shift-`Ins` | 在游標處插入主要鍵的選擇
`Ctrl-X` | 剪下主要鍵的選擇到剪貼簿
`Ctrl-C` (在終端是 `Shift-Ctrl-C` ) | 複製主要鍵的選擇到剪貼簿
`Ctrl-V` | 貼上剪下板的內容到游標處

於此，在 terminal 中主要選取反白文字的區間，使用`Shift-Ctrl-C`去複製，以避免中止正在執行的程式

在現代滾輪滑鼠上的中央滾輪，被認為是中間鍵，並可以被當做中間鍵使用。在 2 鍵滑鼠系統的情況下，同時按左鍵和右鍵就相當於按中間鍵。

為了在 Linux 字元控制台中使用游標，你必須讓`gpm`(8) 在背景執行。

### 1.4.5. 文件內容查看

`less`(1) 命令是一個增強版的分頁程式（檔案內容檢視器）。它按照指定的命令引數或標準輸出來讀取檔案。在用 `less` 命令檢視的時候如果需要幫助可以按 “`h`”。它的功能比 `more`(1) 命令更豐富，透過在指令碼的開頭執行 "`eval $(lesspipe)`" 或 "`eval $(lessfile)`" 它的功能還能變得更加強大。詳細請參考 "`/usr/share/doc/less/LESSOPEN`"。 "`-R`" 選項可以實現原始的字元輸出還可以啟用 ANSI 顏色轉義序列。詳細請參考 `less`(1)。

| 提示
在 `less` 命令中，輸入"`h`"來檢視幫助螢幕，輸入"`/`"或"`?`"來搜尋字串，輸入"`-i`" 來改變大小寫敏感性。

### 1.4.6. 文字編輯器

在使用類 Unix 系統過程中， 各種類似於Vim 或 Emacs的工具，你應該精通其中的一個。

我認為習慣於使用 Vim 指令是一個明智的選擇，因為Linux/Unix系統裡一般都附帶了 Vi 編輯器。 (實際上最初的`vi` 以及後來的 `nvi` 這類工具程式很常見。因為在 Vim 裡提供了`F1`幫助鍵，在同類工具中它的功能更強大，所以我選擇 Vim 而不是其它新出的一些工具。)

假設你不是用 Emacs 就是用XEmacs 作為你的編輯器，其實還有更好的選擇，尤其是在程式設計的時候。 Emacs 還有很多其他的特點，包括新手導讀，目錄編輯器，郵件客戶端等等。當編寫指令碼或程式的時候，它能自動識別當前工作模式所對應的格式，讓使用更加便利。一些人甚至堅持認為Linux系統裡最需要配備的就是 Emacs。花十分鐘來學習 Emacs 可以為後面的工作剩下更多時間。在此強烈推薦學習使用 Emacs 時候直接使用 GNU Emacs 參考手冊。

在實踐應用中所有這些程式都會有一個教學，輸入 "`vim`" 和F1鍵就可以啟動Vim。建議你最好閱讀一下前面的35行。移動游標到 "`|tutor|`" 並按 `Ctrl-]` 就可以看到線上教學。

| 注意
好的編輯器，像 Vim 和 Emacs，可以處理 UTF-8 及其它不常用編碼格式的文字。有個建議就是在 GUI（圖形使用者介面） 環境下使用 UTF-8 編碼，並安裝要求的程式和字型。編輯器裡可以選擇獨立於 GUI（圖形使用者介面）環境的編碼格式。關於多位元組文字可以查閱參考文件。

### 1.4.7. 設置預設文本編輯器

Debian有許多不同的編輯器。我們建議安裝上面提到的`vim`軟體包。

Debian通過指令“`/usr/bin/editor`”提供了對系統預設編輯器的統一存取，因此其它程序（例如`reportbug`(1)）可以調用它。你可以通過下列指令改變它。

    $ sudo update-alternatives --config editor

對於新手，我建議使用“`/usr/bin/vim.basic`”代替“`/usr/bin/vim.tiny`”，因爲它支援語法突顯 (syntax highlighting)。

| 提示
許多程式使用環境變數“`$EDITOR`”或“`$VISUAL`”來決定使用那個編輯器（參見節 1.3.5, “MC 內部編輯器”和節 9.4.11, “自定義被啟動的程式”）。出於Debian系統的一致性考慮，它們被設置到“`/usr/bin/editor`”。（在歷史上，“`$EDITOR`”是“`ed`”，“`$VISUAL`”是“`vi`”。）

### 1.4.8. 使用 vim

最近的 `vim`(1) 用完全的 "`nocompatible`" 選項啟動自己，進入到 `普通` 模式。[1]

表格 1.16. 基本的 Vim 按鍵列表

模式 | 按鍵 | 操作
--- | --- | ---
`普通` | `:help|only` | 顯示 help file
`普通` | `:e filename.ext` | 開啟新的緩衝區來編輯 `filename.ext`
`普通` | `:w` | 由當前緩衝區覆寫原有檔案
`普通` | `:w filename.ext` | 寫入當前緩衝區到 `filename.ext`
`普通` | `:q` | 退出 `vim`
`普通` | `:q!` | 強制退出 `vim`
`普通` | `:only` | 關閉所有其它分割開啟的視窗
`普通` | `:set nocompatible?` | 檢查 `vim` 是否在完全的 `nocompatible` 模式
`普通` | `:set nocompatible` | 設定 `vim` 到完全的 `nocompatible` 模式
`普通` | `i` | 進入 `插入` 模式
`普通` | `R` | 進入 `替代` 模式
`普通` | `v` | 進入 `可視` 模式
`普通` | `V` | 進入 `可視` 行 模式
`普通` | Ctrl-`V` | 進入 `可視` 塊 模式
除了 `TERMINAL-JOB` 外 | `ESC`-鍵 | 進入 `普通` 模式
`普通` | `:term` | 進入 `TERMINAL-JOB` 模式
`TERMINAL-NORMAL` | `i` | 進入 `TERMINAL-JOB` 模式
`TERMINAL-JOB` | Ctrl-`W` `N` (或者 Ctrl-`\` Ctrl-`N`) | 進入 `TERMINAL-NORMAL` 模式
`TERMINAL-JOB` | Ctrl-`W` `:` | 在`TERMINAL-NORMAL` 模式裡進入`Ex`-模式

請使用 "`vimtutor`" 程式來學習 `vim`，透過一個互動式的指導課程。

`vim` 程式基於 模式 輸入的按鍵來改變它的行為。在 `插入`-模式和 `替代`-模式下，輸入的按鍵大部分進入了緩衝區。移動游標大部分在 `普通`-模式下完成。互動選擇在 `可視`-模式下完成。在`普通`-模式下輸入 "`:`" ，改變它的 模式 進入到 `Ex`-模式。 `Ex`-接受命令。

| 提示
Vim 和 Netrw 軟體包可以一起使用。Netrw 同時支援在本地和網路讀寫檔案，瀏覽目錄。用 "`vim .`" (一個點作為引數)來嘗試 Netrw，在"`:help netrw`"讀取它的文件。

`vim` 的高階配置，參見 節 9.2, “定製vim”。

### 1.4.9. 記錄shell活動

shell指令的輸出有可能滾動出了螢幕，並可能導致你無法再查看到它。將shell活動記錄到文件中再來回顧它是個不錯的主意。當你執行任何系統管理任務時，這種記錄是必不可少的。

| 提示
新版本的 Vim (version>=8.2)能夠被用來清晰的記錄 shell 活動，使用`TERMINAL-JOB`-模式。參見 節 1.4.8, “使用 vim”。

記錄shell活動的基本方法是在`script`(1)下運行shell。

嘗試下列例子

    $ script
    Script started, file is typescript

在`script`下使用任何shell指令。

按`Ctrl-D`來退出`script`。

    $ vim typescript

參見節 9.1.1, “清晰的記錄 shell 活動”。

### 1.4.10. 基本的Unix指令

讓我們來學習基本的Unix指令。在這裏，我指的是一般意義上的“UNIX”。任何UNIX複製所衍生的系統通常都會提供等價的指令。Debian系統也不例外。如果有一些指令不像你想的那樣起作用，請不要擔心。如果shell中使用了`別名`，其對應的指令輸出會不同。這些例子並不意味着要以這個順序來執行。

建議嘗試使用非特權使用者帳號來使用下列的指令。

表格 1.17. 基本的Unix指令列表

指令 | 說明
--- | ---
`pwd` | 顯示當前/工作目錄的名稱
`whoami` | 顯示當前的使用者名
`id` | 顯示當前使用者的身份（名稱、uid、gid和相關組）
`file foo` | 顯示“`foo`”文件的文件類型
`type -p commandname` | 顯示“`commandname`”指令的文件所處位置
`which commandname` | 同上
`type commandname` | 顯示“`commandname`”指令的相關資訊
`apropos key-word` | 查找與“`key-word`”有關的指令
`man -k key-word` | 同上
`whatis commandname` | 用一行解釋 “`commandname`” 指令
`man -a commandname` | 顯示“`commandname`”指令的解釋（Unix風格）
`info commandname` | 顯示“`commandname`”指令相當長的解釋（GNU風格）
`ls` | 顯示目錄內容（不包含以 . 點號開頭的文件和目錄）
`ls -a` | 顯示目錄內容（包含所有文件和目錄）
`ls -A` | 顯示目錄內容（包含幾乎所有文件和目錄，除了“`..`”和“`.`”）
`ls -la` | 顯示所有的目錄內容，幷包含詳細的資訊
`ls -lai` | 顯示所有的目錄內容，幷包含inode和詳細的資訊
`ls -d` | 顯示當前目錄下的所有目錄
`tree` | 使用樹狀圖顯示目錄內容
`lsof foo` | 列出處於開啟狀態的檔案 "`foo`"
`lsof -p pid` | 列出被某程序開啟的檔案: "`pid`"
`mkdir foo` | 在當前目錄中建立新目錄“`foo`”
`rmdir foo` | 刪除當前目錄中的“`foo`”目錄
`cd foo` | 切換到當前目錄下或變量“`$CDPATH`”中的“`foo`”目錄
`cd /` | 切換到根目錄
`cd` | 切換到當前使用者的家目錄
`cd /foo` | 切換到絕對路徑爲“`/foo`”的目錄
`cd ..` | 切換到上一級目錄
`cd ~foo` | 切換到使用者“`foo`”的家目錄
`cd -` | 切換到之前的目錄
`</etc/motd pager` | 使用預設的分頁顯示程式來顯示“`/etc/motd`”的內容
`touch junkfile` | 建立一個空文件“`junkfile`”
`cp foo bar` | 將一個現有文件“`foo`”複製到一個新文件“`bar`”
`rm junkfile` | 刪除文件“`junkfile`”
`mv foo bar` | 將一個現有文件“`foo`”重命名成“`bar`”（“`bar`”必須不存在）
`mv foo bar` | 將一個現有文件“`foo`”移動到新的位置“`bar/foo`”（必須存在“`bar`”目錄）
`mv foo bar/baz` | 移動一個現有文件“`foo`”到新位置並重命名爲“`bar/baz`”（必須存在“`bar`”目錄，且不存在“`bar>/baz`”目錄）
`chmod 600 foo` | 使其他人無法讀寫現有文件“`foo`”（並且所有人都無法執行該文件）
`chmod 644 foo` | 使其他人對現有文件“`foo`”可讀但不可寫（並且所有人都無法執行該文件）
`chmod 755 foo` | 使其他人對“`foo`”可讀而不可寫（並且所有人都能執行該文件）
`find . -name pattern` | 使用 shell “`pattern`” 查找匹配的文件名（速度較慢）
`locate -d . pattern` | 使用 shell “`pattern`” 查找匹配的文件名（速度較快，使用定期生成的數據庫）
`grep -e "pattern" *.html` | 在當前目錄下以“`.html`”結尾的所有文件中，查找匹配“`pattern`”的文件並顯示
`top` | 全螢幕顯示行程資訊，輸入“`q`”退出
`ps aux | pager` | 顯示所有正在運行的行程的資訊（BSD風格）
`ps -ef | pager` | 顯示所有正在運行的行程的資訊（Unix system-V風格）
`ps aux | grep -e "[e]xim4*"` | 顯示所有正在運行“`exim`”和“`exim4`”的行程
`ps axf | pager` | 顯示所有正在運行的行程的資訊（ASCII風格）
`kill 1234` | 傳送程式中止訊號給ID爲“1234”的行程
`gzip foo` | 使用 Lempel-Ziv 編碼（LZ77）將“`foo`”壓縮爲“`foo.gz`”
`gunzip foo.gz` | 將“`foo.gz`”解壓爲“`foo`”
`bzip2 foo` | 使用 Burrows-Wheeter 塊排序壓縮算法和 Huffman 編碼將“`foo`”壓縮爲“`foo.bz2`”（壓縮效果比`gzip`更好）
`bunzip2 foo.bz2` | 將“`foo.bz2`”解壓爲“`foo`”
`xz foo` | 使用 Lempel-Ziv-Markov 鏈算法將“`foo`”壓縮爲“`foo.xz`”（壓縮效果比`bzip2`更好）
`unxz foo.xz` | 將“`foo.xz`”解壓爲“`foo`”
`tar -xvf foo.tar` | 從“`foo.tar`”檔案中提取文件
`tar -xvzf foo.tar.gz` | 從被gzip壓縮過的“`foo.tar.gz`”檔案中提取文件
`tar -xvjf foo.tar.bz2` | 從“`foo.tar.bz2`”檔案中提取文件
`tar -xvJf foo.tar.xz` | 從“`foo.tar.xz`”檔案中提取文件
`tar -cvf foo.tar bar/` | 將目錄“`bar/`”中的內容打包到“`foo.tar`”檔案中
`tar -cvzf foo.tar.gz bar/` | 將目錄“`bar/`”中的內容打包到被壓縮的“`foo.tar.gz`”檔案中
`tar -cvjf foo.tar.bz2 bar/` | 將目錄“`bar/`”中的內容打包到“`foo.tar.bz2`”檔案中
`tar -cvJf foo.tar.xz bar/` | 將目錄”`bar/`“中的內容打包到”`foo.tar.xz`“檔案中
`zcat README.gz | pager` | 使用預設的分頁顯示程式來顯示“`README.gz`”壓縮包中的內容
`zcat README.gz > foo` | 將“`README.gz`”解壓後的內容輸出到文件“`foo`”中
`zcat README.gz >> foo` | 將“`README.gz`”解壓後的內容添加到文件“`foo`”的末尾（如果文件不存在，則會先建立該文件）

| 注意

Unix有一個慣例，以“`.`”開頭的文件將被隱藏。它們一般爲包含了調配資訊和使用者首選項的文件。

對於`cd`指令，參見`builtins`(7)。

基本的 Debian 系統的預設分頁顯示程式是`more`(1)，它無法往回滾動。通過指令“`apt-get install less`"安裝`less`軟體包後，`less`(1)會成爲預設的分頁顯示程式，它可以通過方向鍵往回滾動。

"`[`" 和"`]`" 在正規表達式 "`ps aux | grep -e "[e]xim4*"`" 指令中，可以避免`grep`在結果中排除它自己， 正規表達式中的 "`4*`" 意思是空或字元"`4`" ，這樣可以讓 `grep` 既找到 "`exim`" 也找到 "`exim4`"。 雖然 "`*`" 可以用於指令名稱匹配和正規表達式中，但是它們的含義是不一樣的。欲詳細瞭解正規表達式可以參考 `grep`(1)。

作爲訓練，請使用上述的指令來遍歷目錄並探究系統。如果你有任何有關控制臺指令的問題，請務必自行閱讀手冊。

嘗試下列例子

    $ man man
    $ man bash
    $ man builtins
    $ man grep
    $ man ls

手冊的風格可能讓人有點難以習慣，因爲它們都相當簡潔，尤其是比較老舊、非常傳統的那些手冊。但是，一旦你習慣了它，你會欣賞它們的簡潔。

請注意，許多類Unix指令（包含來自 GNU 和 BSD 的）都可以顯示簡短的幫助資訊，你可以使用下列的其中一種方式來查看它（有時不帶任何參數也可以）。

    $ commandname --help
    $ commandname -h

## 1.5. 簡單 shell 指令

現在，你對如何使用 Debian 系統已經有一些感覺了。讓我們更深入瞭解 Debian 系統的指令執行機制。在這裏，我將爲新手做一般的講解。精確的解釋參見`bash`(1)。

一般的指令由有序的組件構成。

1. 初始化此程式之環境變數值（可選）

2. 指令名

3. 參數（可選）

4. 重導向（可選：`>` , `>>` , `<` , `<<` 等等）

5. 控制運算子（可選：`&&` ， `||` ， 換行符號 , `;` , `&` , `(` , `)`）

### 1.5.1. 指令執行和環境變數

一些環境變數的值會改變部分Unix指令的行爲。

環境變數的預設值由PAM系統初始化，其中一些會被某些應用程式重新設定。

- PAM（可插拔身份驗證模組）系統的模組，比如 `pam_env` 模組，可以透過 `/etc/pam.conf`"、 "`/etc/environment`"和"`/etc/default/locale`"設定環境變數。

- 顯示管理器（例如`gdm3`）可以透過"`~/.profile`"給 GUI（圖形使用者介面）會話重新設定環境變數。

- 使用者特有的程式初始化時，可以重新設定在 "`~/.profile`"、"`~/.bash_profile`" 和 "`~/.bashrc`" 中設定的環境變數。

### 1.5.2. “`$LANG`”變量

預設的語言環境是在 "`$LANG`" 環境變數中定義，它在安裝的時候配置為 "`LANG=xx_YY.UTF-8`"，或者在接下來的 GUI（圖形使用者介面）中配置，例如在 GNOME 中是，"設定" → "區域 & 語言" → "語言" / "格式"。

| 注意
目前建議最好用變數 "`$LANG`" 來配置系統環境變數，只有在逼不得已的情況下才用 `$LC_*`" 開頭的變數。

“`$LANG`” 變數的完整的語言環境值由3部分組成：“`xx_YY.ZZZZ`”。

表格 1.18. 語言環境值的 3 個部分

語言環境值 | 說明
--- | ---
`xx` | ISO 639 語言代碼（小寫）例如“en”
`YY` | ISO 3166 國家代碼（大寫）例如“US”
`ZZZZ` | 編碼，總是設置爲“UTF-8”

表格 1.19. locale 推薦的列表

locale 推薦 | 語言（地區）
--- | ---
`en_US.UTF-8` | 英語（美國）
`en_GB.UTF-8` | 英語（大不列顛）
`fr_FR.UTF-8` | 法語（法國）
`de_DE.UTF-8` | 德語（德國）
`it_IT.UTF-8` | 意大利語（意大利）
`es_ES.UTF-8` | 西班牙語（西班牙）
`ca_ES.UTF-8` | 加泰隆語（西班牙）
`sv_SE.UTF-8` | 瑞典語（瑞典）
`pt_BR.UTF-8` | 葡萄牙語（巴西）
`ru_RU.UTF-8` | 俄語（俄國）
`zh_CN.UTF-8` | 漢語（中華人民共和國）
`zh_TW.UTF-8` | 漢語（中國臺灣）
`ja_JP.UTF-8` | 日語（日本）
`ko_KR.UTF-8` | 韓語（韓國）
`vi_VN.UTF-8` | 越南語（越南）

使用 shell 命令列按順序執行下列典型的指令。

    $ echo $LANG
    en_US.UTF-8
    $ date -u
    Wed 19 May 2021 03:18:43 PM UTC
    $ LANG=fr_FR.UTF-8 date -u
    mer. 19 mai 2021 15:19:02 UTC

這裡，`date`(1)程式執行時使用了不同的語言環境值。

- 第一個指令，“`$LANG`” 設定為系統的預設語言環境值 “`en_US.UTF-8`”。

- 第二個指令，“`$LANG`”設置爲法語的 UTF-8 locale 值“`fr_FR.UTF-8`”。

大多數的指令在執行時並沒有預先定義環境變數。對於上面的例子，你也可以選擇如下的方式。

    $ LANG=fr_FR.UTF-8
    $ date -u
    mer. 19 mai 2021 15:19:24 UTC

| 提示
提交一個 BUG 報告的時候，如果使用的是非英語的環境，在 "`LANG=en_US.UTF-8`" 語言環境環境下對命令進行執行和檢查會更好一些。

對於語言環境調配的細節，參見 節 8.1, “語言環境”。

### 1.5.3. "`$PATH`" 變數

當你在 Shell 裡輸入指令的時候，Shell 會在 "`$PATH`" 變數所包含的目錄列表裡進行搜尋，"`$PATH`" 變數的值也叫作 Shell 的搜尋路徑。

在預設的 Debian 安裝過程中，所使用的使用者帳號的 "`$PATH`" 環境變數可能不包括 "`/usr/sbin`" 和"`/usr/sbin`"目錄。例如，`ifconfig` 指令就需要指定完整的路徑 "`/usr/sbin/ifconfig`"。(類似地， `ip` 指令是在 "`/usr/bin`" 目錄下)

可以在 Bash 指令碼檔案 "`~/.bash_profile`" 或 "`~/.bashrc`" 中改變 "`$PATH`" 環境變數的值。

### 1.5.4. "`$HOME`" 變數

很多指令在使用者目錄中都存放了使用者指定的調配，然後通過調配的內容來改變它的執行方式，使用者目錄通常用 "`$HOME`" 變數來指定。

表格 1.20. "`$HOME`" 變數值列表

"`$HOME`" 變數的值 | 程式執行環境
--- | ---
`/` | 初始程式執行的程式（背景程式 daemon）
`/root` | root 使用者許可權 Shell 執行的程式
`/home/normal_user` | 普通使用者許可權Shell執行的程式
`/home/normal_user` | 普通使用者 GUI 桌面選單執行的程式
`/home/normal_user` | 用root使用者許可權來執行程式 "`sudo program`"
`/root` | 用 root 使用者許可權執行程式 "`sudo -H program`"

| 提示
Shell 擴充 "`~/`" 為轉入當前使用者的主目錄，也就是"`$HOME/`"。Shell 擴充 "`~foo/`" 為`foo` 的目錄，也就是 "`/home/foo/`"。

如果 `$HOME` 對你的程式不可用，參見 節 12.1.5, “Shell 環境變數”。

### 1.5.5. 指令列選項

一些指令附帶參數。這些參數以 "`-`" 或 "`--`" 開頭，通常稱之為選項，用來控制指令的執行方式。

    $ date
    Thu 20 May 2021 01:08:08 AM JST
    $ date -R
    Thu, 20 May 2021 01:08:12 +0900

這裡的指令參數 "`-R`" 改變 `date`(1) 指令輸出為 RFC2822 標準的日期字元格式。

### 1.5.6. Shell 萬用字元

經常有這種情況你期望指令成串自動執行而不需要挨個輸入，將檔名擴展為 glob，(有時候被稱為 萬用字元)，以此來滿足這方面的需求。

表格 1.21. Shell glob 模式

shell glob 模式 | 匹配規則之描述
--- | ---
`*` | 不以 "`.`" 開頭的檔名(段)
`.*` | 以 "`.`" 開頭的檔名(段)
`?` | 一個字元
`[…]` | 包含在括號中的任意字元都可以作為一個字元
`[a-z]` | "`a`" 到 "`z`" 之間的任意一個字元都可以作為一個字元
`[^…]` | 除了包含在括號中的任意字元 ( "`1^` 2"除外 )，其它字元都可以作為一個字元

嘗試下列例子

    $ mkdir junk; cd junk; touch 1.txt 2.txt 3.c 4.h .5.txt ..6.txt
    $ echo *.txt
    1.txt 2.txt
    $ echo *
    1.txt 2.txt 3.c 4.h
    $ echo *.[hc]
    3.c 4.h
    $ echo .*
    . .. .5.txt ..6.txt
    $ echo .*[^.]*
    .5.txt ..6.txt
    $ echo [^1-3]*
    4.h
    $ cd ..; rm -rf junk

參見 `glob`(7)。

| 注意
與 shell 通用的檔名匹配方式不同， 使用 "`-name` " 選項的 `find` (1)，其 shell 模式"`*` " ，匹配以"`.` "開始的檔名.（新 POSIX 的特性)

| 注意
BASH 可以使用內建的 shopt 選項如 "`dotglob` "，"`noglob` ","`nocaseglob` ","`nullglob` ","`extglob` "定製全域性行為,使用 `bash` ( 1 )檢視詳細說明。

### 1.5.7. 指令的回傳值

每個指令都會回傳它的退出狀態（變量：“`$?`”）作爲回傳值。

表格 1.22. 指令的退出代碼

指令的退出狀態 | 數字回傳值 | 邏輯回傳值
--- | --- | ---
success | zero, 0 | TRUE
error | non-zero, -1 | FALSE

嘗試下列例子。

    $ [ 1 = 1 ] ; echo $?
    0
    $ [ 1 = 2 ] ; echo $?
    1

| 注意
請注意，success 是邏輯 TRUE ，0（zero）則是它的值。這有些不直觀，需要在這裡提一下。

### 1.5.8. 典型的順序指令和 shell 重導向

讓我們試著記住下面 Shell 指令裡部分指令列所使用的指令習語。

表格 1.23. Shell 指令常見用法

指令常見用法 | 說明
--- | ---
`command &` | 在子 shell 的後臺 中執行 `command`
`command1 | command2` | 通過管道將 `command1` 的標準輸出作爲 `command2` 的標準輸入（並行執行）
`command1 2>&1 | command2` | 通過管道將 `command1` 的標準輸出和標準錯誤作爲 `command2` 的標準輸入（並行執行）
`command1 ; command2` | 執行 `command1` 與 `command2` 循序地進行
`command1 && command2` | 執行 `command1`；若成功，則續執行 `command2` 循序地進行 (若 `command1` 與 `command2` 都成功，則送回成功的訊息)
`command1 || command2` | 執行 `command1`；若不成功，接著執行 `command2` 循序地 (若 `command1` 或 `command2` 成功，則送回成功的訊息)
`command > foo` | 將 `command` 的標準輸出重導向到文件 `foo`（覆蓋）
`command 2> foo` | 將 `command` 的標準錯誤重導向到文件 `foo`（覆蓋）
`command >> foo` | 將 `command` 的標準輸出重導向到文件 `foo`（附加）
`command 2>> foo` | 將 `command` 的標準錯誤重導向到文件 `foo`（附加）
`command > foo 2>&1` | 將 `command` 的標準輸出和標準錯誤重導向到文件 `foo`
`command < foo` | 將 `command` 的標準輸入重導向到文件 `foo`
`command << delimiter` | 將 `command` 的標準輸入重導向到下面的命令列，直到遇到“`delimiter`”（here document）
`command <<- delimiter` | 將 `command` 的標準輸入重導向到下面的命令列，直到遇到“`delimiter`”（here document，命令列中開頭的製表符會被忽略）

Debian 系統是一個多工的作業系統。後臺任務讓使用者能夠在一個 shell 中執行多個程式。後臺行程的管理涉及 shell 的內建指令：`jobs`、`fg`、`bg` 和 `kill`。請閱讀 bash(1) 中的章節：“SIGNALS”、“JOB CONTROL” 和 “`builtins`(1)”。

嘗試下列例子

    $ </etc/motd pager

    $ pager </etc/motd

    $ pager /etc/motd

    $ cat /etc/motd | pager

儘管4個 shell 重導向的例子都會顯示相同的結果，但最後一個例子毫無意義地運行了額外的 `cat` 指令浪費了資源。

shell 允許你使用 `exec` 通過任意一個文件描述符來打開文件。

    $ echo Hello >foo
    $ exec 3<foo 4>bar  # open files
    $ cat <&3 >&4       # redirect stdin to 3, stdout to 4
    $ exec 3<&- 4>&-    # close files
    $ cat bar
    Hello

預定義（Predefined）的文件描述符0-2。

表格 1.24. 預定義的文件描述符 (file descriptors)

設備 | 說明 | 文件描述符
--- | --- | ---
`stdin` | 標準輸入 | 0
`stdout` | 標準輸出 | 1
`stderr` | 標準錯誤 | 2

### 1.5.9. 指令別名

你可以爲經常使用的指令設置一個別名。

嘗試下列例子

    $ alias la='ls -la'

現在，“`la`”是“`ls -al`”的簡寫形式，並同樣會以長列表形式列出所有的文件。

你可以使用 `alias` 來列出所有的別名（參見 `bash`(1) 中的“SHELL BUILTIN COMMANDS”）。

    $ alias
    ...
    alias la='ls -la'

你可以使用 `type` 來確認指令的準確路徑或類型（參見 `bash`(1) 中的“SHELL BUILTIN COMMANDS”）。

嘗試下列例子

    $ type ls
    ls is hashed (/bin/ls)
    $ type la
    la is aliased to ls -la
    $ type echo
    echo is a shell builtin
    $ type file
    file is /usr/bin/file

`ls` 在最近被使用過，而 “`file`” 沒有，因此 “`ls`” 標記為 “hashed”（被錄入雜湊表），即 shell 有一個內部的記錄用來快速存取 “`ls`” 所處的位置。

| 提示
參見節 9.3.6, “有顏色輸出的指令”。

## 1.6. 類 Unix 的文本處理

在類 Unix 的工作環境中，文本處理是通過使用管道組成的標準文本處理工具鏈完成的。這是另一個重要的 Unix 創新 (設計哲學)。

### 1.6.1. Unix 文本工具

這裏有一些在類 Unix 系統中經常使用到的標準文本處理工具。

- 沒有使用正規表達式：

  - `cat`(1) 連接文件並輸出全部的內容。

  - `tac`(1) 連接文件並反向輸出。

  - `cut`(1) 選擇行的一部分並輸出。

  - `head`(1) 輸出文件的開頭。

  - `tail`(1) 輸出文件的末尾。

  - `sort`(1) 對文本文件的行進行排序。

  - `uniq`(1) 從已排序的文件中移除相同的行。

  - `tr`(1) 轉換或刪除字元。

  - `diff`(1) 對文件的行進行對比。

- 預設使用基礎正則表示式（ BRE ）：

  - `ed`(1) 是一個原始行編輯器。

  - `sed`(1) 是一個流編輯器。

  - `grep`(1) 匹配滿足 pattern 的文字。

  - `vim`(1) 是一個螢幕編輯器。

  - `emacs`(1) 是一個螢幕編輯器。（有些擴展的 BRE ）

- 使用擴展的正規表達式（ ERE ）：

  - `awk`(1) 進行簡單的文本處理。

  - `egrep`(1) 匹配滿足多個 pattern 的文字。

  - `tcl`(3tcl) 可以進行任何你想得到的文本處理：參見 `re_syntax`(3) 。經常與 `tk`(3tk) 一起使用。

  - `perl`(1) 可以進行任何你想得到的文本處理。參見 `perlre`(1) 。

  - `pcre2-util` 軟體包中的 `pcre2grep`(1) 可以匹配滿足 Perl 相容正規表達式（PCRE） 模式的文字。

  - `python`(1) 帶有 `re` 模組可以處理每個預期的文件作業。見 "`/usr/share/doc/python/html/index.html`"。

如果你不確定這些指令究竟做了什麼，請使用“`man command`” 來自己把它搞清楚吧。

| 注意
排序的順序和表示式的範圍取決於語言環境。如果你想要獲得一個命令的傳統行為，可以使用 C 語言環境或 C.UTF-8 語言環境代替原來的 UTF-8 語言環境（參見 節 8.1, “語言環境”）。

| 注意
Perl 正規表達式（ `perlre`(1) ）、Perl 兼容正則表達式（PCRE） 和 Python 的`re` 模組提供的正規表達式與一般的 ERE 相比多了許多通用的擴展。

### 1.6.2. 正規表達式

正規表達式被使用在許多文字處理工具中。它們類似 shell 的萬用字元，但更加複雜和強大。

正規表達式描述要匹配的模式，它是由文字字元和元字元 (metacharacters)構成的。

元字元僅僅是帶有特殊含義的字元。它們有兩種主要的形式，BRE 和 ERE ，使用哪種取決於上述的文本工具。

表格 1.25. BRE 和 ERE 中的元字元

BRE | ERE | 正規表達式的描述
--- | --- | ---
`\ . [ ] ^ $ *` | `\ . [ ] ^ $ *` | 通用的元字元
`\+ \? \( \) \{ \} \|` |  | BRE 獨有的“`\`”跳脫元字元
| `+ ? ( ) { } |` | ERE 獨有的不需要“`\`”轉義的元字元
`c` | `c` | 匹配非元字元(即文字字元) “`c`”
`\c` | `\c` | 匹配一個字面意義上的字元 “`c`”，即使 “`c`” 本身是元字元
`.` | `.` | 匹配任意字元，包括換行符
`^` | `^` | 字串的開始位置
`$` | `$` | 字串的結束位置
`\<` | `\<` | 單詞的開始位置
`\>` | `\>` | 單詞的結束位置
`[abc…]` | `[abc…]` | 匹配在 “`abc...`” 中的任意字元
`[^abc…]` | `[^abc…]` | 匹配除了 “`abc...`” 中的任意字元
`r*` | `r*` | 匹配零個或多個 “`r`”
`r\+` | `r+` | 匹配一個或多個 “`r`”
`r\?` | `r?` | 匹配零個或一個 “`r`”
`r1\|r2` | `r1|r2` | 匹配一個 “`r1`” 或 “`r2`”
`\(r1\|r2\)` | `(r1|r2)` | 匹配一個 “`r1`” 或 “`r2`“ ，並作爲括號內的正規表達式

常見的 `emacs` 表示為基本的 BRE 但擴充為 "`+`" 與 "`?`" 做為 metacharacters 當成 ERE。因此，不需要以 "`\`" 在正規的表示 `emacs` 表示中做為跳脫字元。

`grep`(1) 可以使用正規表達式來進行文本搜索。

嘗試下列例子

    $ egrep 'GNU.*LICENSE|Yoyodyne' /usr/share/common-licenses/GPL
    GNU GENERAL PUBLIC LICENSE
    GNU GENERAL PUBLIC LICENSE
    Yoyodyne, Inc., hereby disclaims all copyright interest in the program

| 提示
參見節 9.3.6, “有顏色輸出的指令”。

### 1.6.3. 替換表達式

對於替換表達式，一些字元有特殊的含義。

表格 1.26. 替換表達式

替換表達式 | 替換表達式替換的文本
--- | ---
`&` | 正規表達式所匹配的內容（在 `emacs` 中使用 `\&` ）
`\n` | 前 n 個括號的正規表達式匹配的內容（ “n” 是數字）

但對 Perl 替換字串來說，應使用“$&”而非“&”，應使用“$n”而非“\n”。

嘗試下列例子

    $ echo zzz1abc2efg3hij4 | \
    sed -e 's/\(1[a-z]*\)[0-9]*\(.*\)$/=&=/'
    zzz=1abc2efg3hij4=
    $ echo zzz1abc2efg3hij4 | \
    sed -E -e 's/(1[a-z]*)[0-9]*(.*)$/=&=/'
    zzz=1abc2efg3hij4=
    $ echo zzz1abc2efg3hij4 | \
    perl -pe 's/(1[a-z]*)[0-9]*(.*)$/=$&=/'
    zzz=1abc2efg3hij4=
    $ echo zzz1abc2efg3hij4 | \
    sed -e 's/\(1[a-z]*\)[0-9]*\(.*\)$/\2===\1/'
    zzzefg3hij4===1abc
    $ echo zzz1abc2efg3hij4 | \
    sed -E -e 's/(1[a-z]*)[0-9]*(.*)$/\2===\1/'
    zzzefg3hij4===1abc
    $ echo zzz1abc2efg3hij4 | \
    perl -pe 's/(1[a-z]*)[0-9]*(.*)$/$2===$1/'
    zzzefg3hij4===1abc

請特別注意這些括號正規表達式的格式，以及這些被匹配的文本如何被替換到不同的文本處理工具中的。

這些正規表達式在一些編輯器中也可以用來移動游標和替換文字。

在 shell 指令列行末的反斜槓 “`\`” 會跳脫一個換行字元（作為空白字元），並將游標移動到下一行的行首。

請閱讀所有相關手冊來學習這些指令。

### 1.6.4. 正規表達式的全域性替換

`ed`(1) 指令可以在 “`file`” 中將所有的 “`FROM_REGEX`” 替換成 “`TO_TEXT`” 。

    $ ed file <<EOF
    ,s/FROM_REGEX/TO_TEXT/g
    w
    q
    EOF

`sed`(1) 指令可以在 “`file`” 中將所有的 “`FROM_REGEX`” 替換成 “`TO_TEXT`” 。

    $ sed -i -e 's/FROM_REGEX/TO_TEXT/g' file

`vim`(1) 指令可以通過使用 `ex`(1) 指令在 “`file`” 中將所有的 “`FROM_REGEX`” 替換成 “`TO_TEXT`” 。

    $ vim '+%s/FROM_REGEX/TO_TEXT/gc' '+update' '+q' file

| 提示
上面的 “`c`” 標誌可以確保在每次替換時都進行互動式的確認。

多個檔案（ “`file1`”，“`file2`” 和 “`file3`” ）可以使用 `vim`(1) 或 `perl`(1) 通過正規表達式進行類似的處理。

    $ vim '+argdo %s/FROM_REGEX/TO_TEXT/gce|update' '+q' file1 file2 file3

| 提示
上面的 “`e`” 標誌是為了防止 “No match” 錯誤中斷替換。

    $ perl -i -p -e 's/FROM_REGEX/TO_TEXT/g;' file1 file2 file3

在 perl(1)例子中 , "`-i`" 是在每一個目標檔案的原處編輯，"`-p`" 是表示迴圈所有給定的檔案。

| 提示
使用參數 “`-i.bak`” 代替 “`-i`” ，可以在檔名後新增 “`.bak`” 再儲存。對於複雜的替換，這使得從錯誤中恢復變得容易。

| 注意
`ed`(1) 和 `vim`(1) 使用 BRE ； `perl`(1) 使用 ERE 。

### 1.6.5. 從文字檔案的表格中提取資料

下面有一個文字檔案 “`DPL`” ，裡面含有 2004 年以前 Debian 專案的領導者名字和起始日期，並以空格分隔。

    Ian     Murdock   August  1993
    Bruce   Perens    April   1996
    Ian     Jackson   January 1998
    Wichert Akkerman  January 1999
    Ben     Collins   April   2001
    Bdale   Garbee    April   2002
    Martin  Michlmayr March   2003

| 提示
參見 “Debian 簡史” 獲得最新的 Debian 領導階層歷史。

Awk 經常被用來從這種型別的檔案中提取資料。

嘗試下列例子

    $ awk '{ print $3 }' <DPL                   # month started
    August
    April
    January
    January
    April
    April
    March
    $ awk '($1=="Ian") { print }' <DPL          # DPL called Ian
    Ian     Murdock   August  1993
    Ian     Jackson   January 1998
    $ awk '($2=="Perens") { print $3,$4 }' <DPL # When Perens started
    April 1996

Shell （例如 Bash ）也可以用來分析這種檔案。

嘗試下列例子

    $ while read first last month year; do
        echo $month
      done <DPL
    ... same output as the first Awk example

內建指令 `read` 使用 “`$IFS`” （內部域分隔符）中的字元來將行分隔成多個單詞。

如果你將 “`$IFS`” 改變為 “`:`” ，你可以很好地使用 shell 來分析 “`/etc/passwd`”。

    $ oldIFS="$IFS"   # save old value
    $ IFS=':'
    $ while read user password uid gid rest_of_line; do
        if [ "$user" = "bozo" ]; then
          echo "$user's ID is $uid"
        fi
      done < /etc/passwd
    bozo's ID is 1000
    $ IFS="$oldIFS"   # restore old value

（如果要用 Awk 做到相同的事，使用 “`FS=':'`” 來設定域分隔字元。）

IFS 也被 shell 用來分割參數擴展、指令替換和算術擴充套件的結果。這不會出現在雙引號或單引號中。 IFS 的預設值為 空格、tab 和換行字元。

請謹慎使用 shell 的 IFS 技巧。當 shell 將指令碼的一部分解釋為對它的輸入時，會發生一些奇怪的事。

    $ IFS=":,"                        # use ":" and "," as IFS
    $ echo IFS=$IFS,   IFS="$IFS"     # echo is a Bash builtin
    IFS=  , IFS=:,
    $ date -R                         # just a command output
    Sat, 23 Aug 2003 08:30:15 +0200
    $ echo $(date -R)                 # sub shell --> input to main shell
    Sat  23 Aug 2003 08 30 36 +0200
    $ unset IFS                       # reset IFS to the default
    $ echo $(date -R)
    Sat, 23 Aug 2003 08:30:50 +0200

### 1.6.6. 用於管道指令的小片段指令碼

下面的指令碼作為管道的一部分，可以做一些細緻的事情。

表格 1.27. 管道指令的小片段指令碼列表

指令碼片段（在一行內輸入） | 指令效果
--- | ---
`find /usr -print` | 找出"`/usr`"下的所有檔案
`seq 1 100` | 顯示 1 到 100
`| xargs -n 1 command` | 把從管道過來的每一項作為參數，重複執行指令
`| xargs -n 1 echo` | 把從管道過來的，用空格隔開的項，分隔成多列
`| xargs echo` | 把從管道過來的所有列合併為一列
`| grep -e regex_pattern` | 從管道過來，包含有 regex_pattern的列，提取出來
`| grep -v -e regex_pattern` | 把從管道過來，不包含有 regex_pattern的列，提取出來
`| cut -d: -f3 -` | 把從管道過來，用 "`:`"分隔的第三行提取出來 (passwd 檔案等。)
`| awk '{ print $3 }'` | 把用空格隔開的第三行提取出來
`| awk -F'\t' '{ print $3 }'` | 把用 tab 鍵隔開的第三行提取出來
`| col -bx` | 刪除退格鍵，擴展 tab 鍵為空格鍵
`| expand -` | 擴展 tab 鍵為空格鍵
`| sort| uniq` | 排序並刪除重複列
`| tr 'A-Z' 'a-z'` | 將大小字母轉換為小寫字母
`| tr -d '\n'` | 將多列連線為一列（移除換行字元）
`| tr -d '\r'` | 刪除換行CR字元
`| sed 's/^/# /'` | 在每列行首增加一個"`#`"符
`| sed 's/\.ext//g'` | 刪除 "`.ext`"
`| sed -n -e 2p` | 顯示第二列
`| head -n 2 -` | 顯示最前面兩列
`| tail -n 2 -` | 顯示最後兩列

使用 `find`(1) 和 `xargs`(1)，單行 shell 指令碼能夠在多個檔案上迴圈使用，可以執行相當複雜的任務。參見 節 10.1.5, “查詢檔案的語法” 和 節 9.4.9, “使用檔案迴圈來重複一個指令”.

當使用 shell 互動模式變得太麻煩的時候，請考慮寫一個 shell 腳本(參見 節 12.1, “Shell 腳本”).

[1] 即使舊的 `vim` 也能夠啟動完全的 "`nocompatible`" 模式，透過使用 "`-N`" 選項啟動。

序 |  | 章 2. Debian 軟體包管理