shell scripting

Classic Shell Scripting 讀書筆記(一)

  • Devops

入門

printf

  • 格式聲明 (format specfications) 是一種佔位符號 (placeholder),結構包含 1) 百分比符號 (%) 2) 指示符 (specifier),常用的有字符串 %s 及十進位整數 %d

tr

範例:Translate DOS file to UNIX

  • tr -d: 自 stdin 刪除 source-char-list 的字符
  • \r: ASCII carriage return

/dev/tty

範例:Read password via /dev/tty

  • 當程序打開 /dev/tty 時,UNIX 會將它重定向到一個終端再與程序結合,該終端可以是 1)實體的 console 2) 串行端口 (serial port) 3) 偽終端 (pseudoterminal)
  • stty (set tty) 用來控制終端的設置,echo/-echo 選項用來開關自動打印輸入的功能

i18n and l10n

  • 當 i18n 作為設計軟體的過程時,無須再修改軟體或重新編譯程式代碼,就可以給特定的群體使用
  • 當 l10n 作為設計軟體的過程時,目的是讓特定的使用者可以使用軟體。其中包含翻譯輸出的文字、貨幣、日期、時間、單位等格式
  • 對使用者來說,用來控制讓哪種語言或文化環境生效的功能,叫做 locale
  • 除了 CPOSTFIX 以外,locale 名稱並未標準化
  • BSD 與 Mac OS X 完全不支援 locale
  • locale 的支援仍未成熟:Shell 腳本常受到 locale 影響;在大多數 UNIX 系統下,很難從 locale 文件與工具來判定字元集 (character class)、等價字元集 (equivalence class) 實際上包含了哪些字元,以及有哪些排序符號 (collating symbol) 可用。
  • Shell 腳本開發者應了解 locale 對他們代碼所造成的影響

列出所有 locales

取得特定 locale 的資訊

定義 LC_ALL 來覆寫預設的 locale,可查詢的變量有日期時間格式 LC_TIME、貨幣格式LC_MONETARY

Matching text

  • 1992 POSFIX 整合了 grepegrepfgrep,透過不同選項控制
    • 最早的 grep: 使用 BRE (Basic Regular Expression),只能匹配單一正則
    • egrep: 使用 ERE (Extended Regular Expression),只能匹配單一正則
    • fgrep: 不使用正則,使用特定演算法可以同時匹配多個固定字串

Reqular expression

一些重要的 POSIX BRE/ERE 的 meta 字元

Unix 程序使用何種類型

Bracket expression

UNIX 的範圍表達式在 POSIX 的標準下,現在叫做方括號表達式。其中包含三種字元集構造:

  • 字元集 (character class): [:關鍵字:],關鍵字描述不同的字元集,如 alphadigit,例如 [[:alpha:]!] 匹配任一英文字母或驚嘆號
  • 排序符號 (collating symbol): [.排序元素.],受特定 locale 影響,在非英語系的語言,某些成對的字元必須視為單個字元,例如在捷克或西班牙語系,ch 兩個字元會保持連續狀態,在 locale 支持下,可用於如 [ab[.ch.]de] 的匹配
  • 等價字元集 (equivalence class):[=字元=],列出應視為等值的一組字元,如在法文的 locale 裡,[[=e=]] 可能匹配 e、é、ë

BRE

  • 在前面放置 \ 用來轉義 meta 符號,常用的有 \\\[
  • 在方括號表達式中,^ 放在字首是取反 (complement) 的意思
  • 方括號表達式可以表達字元的範圍,如 [0-9a-fA-F],但範圍表示法是根據機器中字元集的數值(ASCII vs EBCDIC),有可移植性的問題。建議用 POSIX 字元集表示法取代。
  • 在方括號表達式中,meta 符號不再具備特殊含意
  • 後向引用 (backreference) 的結構 \( \)\digit 於後方調用,例如匹配所有引號括起來的字 \(["']\).\1
  • 錨點 (anchor) meta 字元 ^$,分別用來針對字串的開始/結尾處進行匹配

範例:預先消除空行

BRE 運算元優先級 (precedence)

由高至低排列

  1. [..][::][==]
  2. \metacharacter
  3. []
  4. \(\)\digit
  5. *\{\}
  6. no symbol,表示連續
  7. ^$

ERE

  • 以匹配單個字串來說,與 BRE 一致,不同之處在於匹配多個字串方面
  • 不存在後向引用
  • ?+ 可以更細膩處理匹配控制
  • 方括號表達式用來「匹配此字串,或其他字串」;交替運算符| 用來「匹配這個序列,或其他序列」
  • () 用來分組
  • ^$ 永遠是 meta 符號,與 BRE 不同,若放在字串中間會匹配不到任何東西

範例:匹配多個連續出現的 read 或是 write,且中間可以是空白

ERE 運算元優先級 (precedence)

  1. [..][::][==]
  2. \metacharacter
  3. []
  4. ()
  5. *+?{}
  6. no symbol
  7. ^$
  8. |

文本替換(substitution) sed

  • / 扮演定界符號(delimiter),分隔正則與替代文本
  • 除了 /,任何可顯示的符號都可以當定界符號,在處理文件名稱時,通常用標點符號 ;:, 取代 /,因為轉義完路徑看起來會很醜
  • 在 s 命令最後,若以 g 結尾,代表全域(global)取代,若以數字結尾,則表示「第 n 個匹配才取代」
  • sed 會記住 -f 腳本裡遇到的最後一個正則,透過使用空的正則,可重複利用同一個正則
  • 命令裡的每個文件會依序打開與讀取,如果沒有文件,則用標準輸入,或用單個破折號 - 表示標準輸入
  • 讀取文件時,將讀取的行放到記憶體某個位置,稱為模式空間(pattern space),將一次次修改的結果覆蓋在相同位置,最後輸出模式空間裡的最後內容

範例:擷取第一個字段(將第一個冒號之後的文本取代為空字串)

範例:將 /home/tolstoy 的目錄結構建立一份副本在 /home/lt 底下,並追蹤執行

  • 使用產生命令(generating commands)的手法,使命令內容成為 shell 的輸入

範例:轉換 HTML 為 XHTML

範例:模擬 grep 的功能,只打印含 的行

addressing

默認情況下,每一個編輯命令(editing command)會套用到每個輸入行;要限制應用到哪些行,須在命令前加個地址(address),以下為不同種類的地址;注意 address 限用單引號

  1. 正則表達式

    範例:為部份代碼增加註釋

  2. 最終行(符號 $ 在 sed 裡表示「最終行」)

    範例:打印文件最終行

  3. 絕對的行號

    範例:模擬 head 功能,q 命令要求 sed 馬上離開,不再讀取其他輸入

  4. 範圍(以逗號隔開兩個絕對行號或正則)

    範例:只打印 10~42 行

    範例:只替代範圍內的行

  5. 否定正則

    注意 ! 後面最好不要加上空格,因為會導致某些老舊的 sed 版本無法識別

    範例: 將沒有 used 的每個行裡的 new 取代為 used

改變定界符

範例:以 : 隔開正則,以 ; 扮演 s 命令的定界符

Longest leftmost 從最長最左邊匹配原則

POSIX 標準指出:「完全一致的匹配,指的是自最左邊開始、針對每個子模式、由左至右,必須匹配到最長的可能的字串」

留意 null 字串的匹配

選定字段 cut

因行內的每個字段長度不一,使用字元 -c 剪下資料的風險較大,一般偏好使用字段 -f 配合定界符 -d 為基礎來提取資料

連接字段 join

範例:刪除註解、取代分隔符為 whitespace 並排序後合併文件

重新編排字段 awk

  • 程序架構為 pattern {action},pattern 通常是以 / 定界的 ERE,action 為 awk 語句,意思是「如果 pattern 為真,則執行 action」

  • 記得在參數間用逗號分隔,否則字段會連在一起

  • BEGINEND 模式

範例:以自訂分隔符輸出特定欄位

範例:自訂變數、使用 printf 混和文本