Skip to content

Ops

Classic Shell Scripting 讀書筆記 (八)

  • Ops

ksh 與 bash 的擴展與相容性

模式匹配的擴展

bash 要先啟用 extglob 選項才支援此功能

正則的 ^$ 在 Shell 的模式匹配沒有等同物,我們可以想像,在 Shell 的模式匹配中, ^$ 已經自動帶在前後文了,若在模式前後加上 * 則可以停用此功能

括弧展開

源自於 csh 的功能,ksh 跟 bash 都支持此用法,可以巢狀化

Read More »Classic Shell Scripting 讀書筆記 (八)

Classic Shell Scripting 讀書筆記 (七)

  • Ops

進程

程式 (program) 的一個實例 (instance),由 fork()execve() 等系統調用所起始、執行、直到下達 exit() 系統調用為止

UNIX 支援多進程,由文本切換實現(context switch),進程本身不管文本切換,也沒有必要在程式裡撰寫撤回控制權給操作系統的處理

系統內核的調度器(scheduler)負責管理進程的執行,並參考進程的優先權決定順序

平均負載(load average)

在任何瞬間,等待執行的進程平均數,當平均負載持續地超出可用 CPU 的承載時,表示系統已經超載

由於會一直變化,uptime 指令分別回報最後一分鐘、五分鐘、十五分鐘的估值

建立進程

UNIX 最大的貢獻,就是能輕易建立進程

很多進程由 Shell 啟動——每個命令行的第一個單詞代表要執行哪個程序,且保證具備以下事項:

  • 內核本文(kernel context),存在內核的數據結構,紀錄進程的資訊,方便管理與控制進程
  • 一個私有的(private)、被保護的(protected)的虛擬位址空間,確保進程間不互相干擾。其可以是主機的可定址空間,可能受限於 Swap 、其他執行中工作的大小、系統調校參數的設置等
  • 三個皆以開啟的文件描述代碼(標準輸入、標準輸出、標準錯誤輸出)
  • 起始於交談模式 Shell 的進程,會有一個控制終端(controlling terminal),扮演三個標準文件數據流的默認來源與目的地
  • Shell 展開命令行中的參數,省去程序的負擔且提供統一性
  • 記憶體中的一個環境變量區域(environment space)會存在,透過函式庫調用取得

進程編號

編號為 0 的進程稱為 kernel、sched 或 swapper,可能不會顯示在 ps 列表中

進程的形式是樹狀的,除了 kernel 以外,每個進程都有父進程,及零至多個子進程

編號為 1 的進程稱為 init,對於父進程過早消失(die)的進程,其父進程會重新被指派給 init

系統在正常關機時,進程的刪除是編號由大到小依次執行的,直到剩下 init 為止,當 init 結束,系統終止

Read More »Classic Shell Scripting 讀書筆記 (七)

Classic Shell Scripting 讀書筆記 (六)

  • Ops

文件與文件系統

簡單來說,文件是計算機系統裡的一堆數據,可以用單一實體的方式被引用

文件命名

原始的 UNIX 文件系統設計者,決定將ASCII 256 個元素集合都可用於文件名,但有兩個例外:

  • 控制字符 NUL(此字符所有位址皆為0),這是許多程式語言用來表示字串結尾的字符
  • 斜槓(/)字符

最好考慮加上以下限制:

  • 是可視字符
  • 避免使用 Shell 的 meta 字符,也就是大部分的標點符號
  • 避免用連字號開頭,看起來像是 UNIX 的命令選項

UNIX 文件名是 Case Sensitive,慣用小寫,除了重要文件會用大寫或大小寫混用,如 READMEMakefile,原因是在 ASCII 裡,大寫排在小寫之前,會列在前方(現行系統排序則是參考 locale)

命名長度普遍允許使用到 255 個字符,POSIX 中定義 NAME_MAX 來限制其長度

ASCII

1963 年,美國標準學會以 American Standard Code for Information Interchange 名稱提出 7 位元的字符集,允許 128 個不同字符。

7 位元對世界語言是不夠的,由於現在系統都使用 8 位元作為最小定址儲存單位,允許 256 個不同字符,前半段被拿來客製化,後半段留給 ASCII,在未遵循國際標準的情況下,也因此出現了幾百種不同的字符指定方式,或稱內碼頁(code page)。

8 位元對歐洲語系仍是不夠的,因此 ISO 為此開發了一系列的代碼頁。

90 年代,單一萬國字符集 Unicode 開始運作,所有字符最終需要大約 21 個位元。由於許多操作系統只使用到 16 個位元,UNIX 系統使用一個可變動的位寬度編碼: UTF-8,允許已存在的 ASCII 文件成為有效的 Unicode 文件。

文件裡有什麼

以另一個觀點來看,UNIX 文件不過是 0 個或多個不知名數據字節所集結而成的字節流

複製一個文件:

許多工具設計上使用「大的但大小固定」的緩衝區來保存文本行,如果輸入過長的行,可能導致錯誤,建議長度限制在易讀的範圍,例如 50-70 個字符

所有文件被視為是二進制文件:每一個包含在其中的字節,都有 256 種可能的值。

文本文件(有分行的文本)可視為二進制文件的子集,以 ASCII linefeed (LF) 表示行的界線,也就是換行字符,在程式語言通常以 \n 來表示(比起 Windows 使用一組 carriage-return/linefeed 簡單多了)

文件中會保留字節數的計數,當嘗試讀取超越此計數時,返回 end-of-line 的暗示,因此不可能看到任何磁盤區塊之前的內容

文件系統架構

UNIX 文件系統是可嵌套的樹狀結構,目錄使用 directory 而非 folder(偏向紙本的),結構的根源為根目錄,使用特殊名稱 /

當目錄底下有過多的文件,應該以子目錄重新組織,提昇查找效率

文件名完整路徑長度沒有特殊限制,POSIX 中定義 PATH_MAX 來限制其長度,通常為 256 個字符

UNIX 目錄本身就是文件,但擁有特殊屬性且有特定訪問方式

所有 UNIX 目錄,就算是空的,也總是包含兩個特殊目錄: ..(父目錄) 及 . (當前目錄本身),根目錄的父目錄就是自己(//..是一樣的)

路徑結尾若以斜槓 / 結束,則該文件是一個目錄,沒以斜槓結尾,不一定不是目錄

WWW 的 URL 結構就是 UNIX 風格的

層級式文件系統

UNIX 允許將某個文件系統,邏輯性地放置於令一個文件系統的任意目錄之上,稱為掛載(mounting)

掛載的相關細節,除存在一個特殊文件中,通常為 /etc/fstab/etc/vfstab

有些掛載/卸載需要特殊權限,有些則允許非特定用戶也可以操作,如 CD-ROM、隨身碟

Index Node (inode)

文件系統建立時,一個管理原指定的固定大小表格也隨之建立,稱為 inode

inode 包含了系統辨識文件時所需的 metadata,但文件名不包含在內,文件名保存在目錄裡

  • 列出目錄下的文件時,不須多次查詢 inode
  • 一個 inode 編號可以對應到多個文件名,也就是 UNIX 中的連結(link)功能

inode 所保存的訊息包含文件的: 1) inode 序列號 2) 類型 3) 連結 4) 大小 5) 權限 6)時間戳

當一個物理文件,其有多個名稱時(代表至少存在一個連結),哪一個才能刪除物理文件?——
inote 表包含了連接到文件的計數,當計數為 0,文件區塊最終才會重新指派給可用空間的列表

軟連接與硬連接

同一個文件系統下的連接,指向的是 inode 編號(hard-link),但連結跨越文件系統時,inode 會紀錄該文件類型是符號連接/軟連接(symbolic/ soft link),指向的是「一個 UNIX 路徑」而非 inode 編號

為了避免早成死循環,目錄通常不能有硬連接,除了 ...

Read More »Classic Shell Scripting 讀書筆記 (六)

Classic Shell Scripting 讀書筆記 (五)

  • Ops

AWK

  • 所有 UNIX 系統裡都至少有一套 awk
  • awk 是 POSIX 的一部分
  • 把輸入流看成一連串紀錄(record)的集合,每筆紀錄可以更進一步細分成字段(field)
  • 如何構成一筆紀錄和一個字段,是可控制的,甚至可以在處理期間修改
  • 替使用者妥善處理每個文件的開啟、讀取與關閉
  • 強大的功能大多是具備對正則表達式的支援

命令行

  • 慣例是將 -F 當作第一個選項,或者也可以設定變量 FS 來重新定義分隔符

    以上面例子來看,-F 應用到第一個文件組,而 FS 應用到第二個文件組

  • -- 是特殊選項,指出 awk 本身已經沒有更進一步的命令行選項

  • 初始化的 -v 必須放在 program 之前,在文件處理之前生效
  • program 使用單引號,保護內容不被 Shell 解釋,不過要特別注意程序是否本身包含單引號

程序元素

  • 提供標量(scalar)、陣列(array)兩種資料結構
  • 提供語句類型:賦值、註釋、條件、函數、輸入、循環、輸出

字串表達式

  • 以雙引號定界
  • 長度沒有任何限制,視內存而定
  • 賦值後舊字串的內存空間會自動回收
  • 字串的比較視字串長短而定,如 “A” < “AA” 返回 1
  • awk 無字串連接的運算符,多個連續的字串,會自動連接在一起
  • 數字轉字串,可透過連接空字串,如 s = “” + 123

正則匹配語句

  • 提供兩個運算符: ~!~ 分別代表匹配和不匹配
  • 正則常量可以用雙引號或斜槓 / 定界,斜槓形式比較常見,因為可以用來強調括起來的就是正則表達式
  • 字面意義的引號、斜槓、反斜槓在正則裡都應該被保護,兩種形式的保護方式可能不同,例如 "\\\\TeX"/\\Tex/ 都是表示正則的 \Tex

數值表達式

  • 所有 awk 裡的數字都以雙精確度的浮點值(對應於 C 的 double)表示
  • awk 在 IEEE 754 廣泛可用前就以開發,無法完整支持 Infinity 與 NaN
  • 浮點數可以包含一個末端以字母 e(或 E)所表示的十次方指數,例如 0.03125、3.125e-2、0.003125E1
  • 字串轉數字,可透過連接0,如 n = 0 + “123”

Read More »Classic Shell Scripting 讀書筆記 (五)

Classic Shell Scripting 讀書筆記 (四)

  • Ops

波浪展開

  • 若命令行第一個字串開頭為波浪符號 (~),則執行波浪展開,目的是轉換使用者根目錄的絕對路徑
  • 可以直接或間接方式指定展開的使用者,使用 ~ 則轉換當前使用者,使用 ~{username} 則會從 /etc/passwd 查找特定用戶的根目錄並替換
  • 好處是簡潔,及避免在程序裡把路徑寫死,壞處是可移植性差,許多商用 UNIX 的 Bourne Shell 不支援

通配符 wildcard

  • Shell 會將命令行提供的模式,至換成符合模式的一組排序過的文件名
  • 範圍表示法雖然方便,但你不應該對包含在範圍的字串有太多的假設,比較安全的方式是:分別指定所有大寫字母、小寫字母、數字、或子範圍,避免使用像 [a-Z][A-z] 這樣的用法
  • 使用範圍更大的問題是不同 locale 間的可移植性
  • 習慣上,當執行通配符展開時,UNIX Shell 會忽略以點號(.)開頭的文件,這些文件通常是配置文件或啟動文件

基本通配符

命令替換 command substitution

  • 將命令替換語句替換為執行結果
  • 形式有兩種:使用反引號(`),或將命令括在 $() 裡,在內嵌的用法上,第二種用法有利於增加可讀性,且內嵌的雙引號不須再進行(\)轉義

範例:根據使用的 shell 歸類使用者

expr 命令

  • 「UNIX 少數設計得不嚴謹卻又難用的命令」,不建議使用,可以以 test 或 $((...)) 代替
  • 通常在命令替換語句中使用,通過打印的方式將值返回標準輸出
  • 支持 32 及 64 位元的算術運算,幾乎不會有 overflow 的問題

引用 quoting

用來防止 Shell 將某些你想要的東西解釋成不同意義,有三種方式:
* 反斜槓轉義
* 單引號:強制將字符都看作字面上的意義(即便是反斜槓),不可再內嵌單引號
* 雙引號:同單引號的引用功能,差別在於,使用雙引號引用會確切處理轉義字符、變量、算術、命令替換,雙引號其中的單引號不具特殊意義

Read More »Classic Shell Scripting 讀書筆記 (四)

Classic Shell Scripting 讀書筆記 (三)

  • Ops

test 命令

  • POSIX 將 test 的參數描述為「表達式」:
    • 一元(unary)表達式:由看似一個選項(如-d)與相對應的運算數組成(基本上是一個文件名)
    • 二元(binary)表達式:兩個運算數與一個內嵌的運算元組成,做某種比較操作
  • test 命令有另一種形式 [ expression ],方括號與表達式一定要以空白隔開
  • 表達式可以前置 ! 表示否定

在 XSI 兼容的系統裡,-a 意義等同於 &&; 為了可移植性,建議使用多重條件而非 -a, -o

使用 test 的訣竅

  • 須有參數(避免 null),因此變量展開都要以引號括起來
  • 為了可移植最大化,可以替比較字符串加上前綴 X,例如 if [ "X$answer" = "Xyes" ],可以避免字符串為空或開頭帶減號而混淆 test 命令
  • test 是可以被愚弄的,例如 test -r a_file && cat_file,a_file 可能會在執行 test 與執行 cat 之間改變
  • 只能做整數測試

範例:檢查輸入參數

Read More »Classic Shell Scripting 讀書筆記 (三)