Shell 基本常識

shadow_D發表於2023-01-09

Shell

所有 Linux 發行版預設的 shell 都是 bash shell,在本文側重於基礎的 GNU bash shell 下面是其他幾種流行的 shell

  • ash: 簡單的輕量級 shell 完全相容 bash shell

  • korn: 相容 Bourne shell 的程式設計 shell

  • tcsh: 融入部分 C 語言特性

  • dash:

    • Debian Linux 發行版與其許多衍生產品 dash shell,它是 ash shell 的直系後裔,是 Unix 系統中 Bourne shell 的簡易複製品

    • 在許多基於 Debian 的 Linux 發行版中,dash shell 實際上並不是預設 shell

    • 由於 dash 以簡潔為目標,因此其使用的環境變數比 bash 明顯要少,但 dash 環境中無法使用的 bash 特性

      • 算術運算
      • test 命令不同
      • 不支援 function 語句
  • zsh:

    • 結合 bash, korn, tcsh 的特性的高階 shell

    • 對比 bash 另一個流行的 shell,它汲取了所有現存 shell 的設計理念,增加了許多獨有的特性

    • 是為程式設計師而設計的一款高階 shell

    • 獨有特性

      • 改進的 shell 選項處理
      • shell 相容性模式
      • 可載入模組
    • 到目前為止,zsh shell 是所有 shell 中可定製性最強的

    • 可以輕鬆地執行數學函式

進入命令列

在圖形化桌面出現之前,系統互動的唯一方式就是透過 shell 提供的 文字命令列介面 command line interface,CLI

  • 控制檯終端

    • 該模式只在顯示器上提供一個簡單的 shell CLI,稱作 Linux 控制檯,因為它模擬的早期的硬接線控制檯終端
    • Linux 系統啟動時會自動建立多個 虛擬控制檯,虛擬控制檯是執行在Linux系統記憶體中的終端會話,多數 Linux 發行版會啟動 5~6 個(甚至更多) 虛擬控制檯 代替 啞終端
    • 在大多數 Linux 發行版中,可以使用簡單的按鍵組合來訪問某個 Linux 虛擬控制檯,通常必須按下Ctrl+Alt 組合鍵再按一個功能鍵(F1~F7)來進入你要使用的虛擬控制檯

    注意:在 Linux 虛擬控制檯中是無法執行任何圖形化程式的,儘管虛擬控制檯只是一個文字模式的控制檯終端,但你也可以修改文字和背景色

    • setterm --inversescreen on 作用是文字色和背景色交換,使用 setterm --inversescreen off 可以關閉
    • setterm -background 將終端的背景色改為指定顏色 setterm -foreground 將終端的前景色改為指定顏色,引數: black, red, green, yellow, blue, magenta, cyan, white 共 8 種顏色
    • setterm -reset 可以恢復預設設定
  • 圖形化終端

    • 虛擬控制檯終端的另一種替代方案是使用 Linux 圖形化桌面環境中的 終端模擬軟體包,終端模擬軟體包會在桌面圖形化視窗中模擬控制檯終端

    • 一些流行的圖形化終端模擬器軟體包

      • Alacritty
      • cool-retro-term
      • GNOME Terminal
      • Guake
      • Konsole
      • kitty
      • rxvt-unicode
      • Sakura
      • st
      • Terminator
      • Terminology
      • Termite
      • Tilda
      • xterm
      • Xfce4-terminal
      • Yakuake
    • 常用的 GNOME Terminal, Konsole, xterm

啟動 shell

  • GNU bash shell 是一個程式,提供了對 Linux 系統的互動式訪問,系統啟動的 shell 程式取決於使用者賬戶的配置,在 /etc/passwd 檔案包含了所有系統使用者賬戶以及每個使用者的基本配置資訊

  • 儘管 bash shell 會在登入時自行啟動,但是否會出現 CLI 取決於所使用的登入方式

    • 採用的是虛擬控制檯終端登入,那麼 CLI 提示符會自動出現
    • 透過圖形化桌面環境登入Linux系統,則需要啟動圖形化終端模擬器來訪問 shell CLI 提示符
    • 預設的 bash shell 提示符是美元符號 $,不同的 Linux 發行版會採用不同格式的提示符,shell 提示符並非一成不變
    • 當你登入系統並獲得 shell CLI 提示符後,shell 會話會從你的主目錄開始
  • 大多數 Linux 發行版自帶線上手冊,可用於查詢 shell 命令以及其他 GNU 實用工具的相關資訊,man 命令可以訪問 Linux 系統的手冊頁。

    man 命令之後跟上想要檢視的命令名,就可以顯示相應的手冊頁

    man 命令名
    
    • 當你使用 man 命令檢視命令手冊頁的時候,其中的資訊是由 分頁程式 pager 來顯示的

    • 可以按 q 鍵 退出手冊頁

    • 手冊頁將與命令相關的資訊分成了多段,每一段的慣用名標準,另外有些命令使用的段名並沒有在上面的慣用標準中列出

      • Name: 命令名稱及簡要描述
      • Synopsis: 命令語法
      • Configuration: 命令配置資訊
      • Description: 命令的基本描述
      • Option: 命令選項描述
      • Exit Status: 命令退出狀態
      • Return Value: 返回值
      • Errors: 錯誤資訊
      • Environment: 環境變數
      • Files: 使用的檔案
      • Versions: 版本資訊
      • Conforming To: 遵循的命名標準
      • Notes: 其他幫助資料
      • Bugs: 提交 Bug 的途徑
      • Example: 命令用法示例
      • Authors: 開發人員資訊
      • Copyright: 原始碼版權資訊
      • See Also: 類似命令
    • man 命令可以使用 關鍵字 來搜尋手冊頁

      man -k keyword
      
    • 手冊頁中還有不同的節,每節都分配了一個數字,從 1~9 章節

      # 閱讀方法
      man num intro       # num 是每節的數字
      
      • 1: 可執行程式或 shell 命令
      • 2: 系統呼叫
      • 3: 庫呼叫
      • 4: 特殊檔案
      • 5: 檔案格式約定
      • 6: 遊戲
      • 7: 概念,約定,雜項
      • 8: 超級使用者和系統管理員相關命令
      • 9: 核心執行緒 routine

      Linux 系統手冊頁可能包含一些非標準的節編號

    • 大多數命令接受 -h 或 --help 選項

  • 瞭解如何在命令列中輸入該命令

    COMMAND-NAME [OPTION]... [ARGUMENT]...
    
    • COMMAND-NAME 命令名稱
    • OPTION 修改命令列為的選項
    • ARGUMENT 是傳遞給命令的引數
    • [] 代表命令的必要性,有意味可選非必要
    • ... 表示可以一次或指定多

常用命令

  • cd: 目錄切換,允許絕對路徑或相對路徑
  • pwd: 命令可以顯示出 shell 會話的當前目錄
  • ls: 顯示當前目錄下的檔案和目錄,允許使用萬用字元
  • touch: 建立好指定的檔案並將你的使用者名稱作為該檔案的屬主
  • mkdir: 建立好指定的目錄並將你的使用者名稱作為該檔案的屬主
  • ln: 建立連結檔案
  • cp: 複製檔案,複製目錄需要 -R 選項,格式 cp src dest,其中 src 允許使用萬用字元
  • mv: 移動目錄或檔案,可以起到重新命名作用,該操作不改變檔案的 inode 編號或時間戳
  • rm: 刪除檔案或目錄

檢視檔案內容

  • file: 能夠探測檔案的內部並判斷檔案型別
  • cat: 顯示文字檔案中所有資料
  • more: 分頁檢視
  • less: more 升級版,能夠實現在文字檔案中前後翻動,還有一些高階搜尋功能,還可以在完成整個檔案的讀取之前顯示檔案的內容
  • tail: 顯示檔案最後幾行的內容,預設 10 行
  • head: 顯示檔案開頭幾行的內容,預設 10 行

系統管理命令

  • ps: 監測程式,預設只顯示執行在當前終端中屬於當前使用者的那些程式

    • PID: 程式的 程式 ID process ID,PID
    • TTY: 從屬終端
    • TIME: 其佔用的 CPU 時間
    • CMD: 程式名稱

    Linux 系統中使用的 GNU ps 命令支援以下3種型別的命令列選項:

    • Unix 風格選項,選項前加單連字元

      需要檢視系統中執行的所有程式,可以使用 -ef 選項組合

      • -e 選項指定顯示系統中執行的所有程式

      • -f 選項則擴充輸出內容以顯示一些有用的資訊列

        • UID: 啟動該程式的使用者
        • PID: 程式 ID
        • PPID: 父程式的 PID(如果該程式是由另一個程式啟動的)
        • C: 程式生命期中的 CPU 利用率
        • STIME: 程式啟動時的系統時間
        • TTY: 程式是從哪個終端裝置啟動的
        • TIME: 執行程式的累計 CPU 時間
        • CMD: 啟動的程式名稱
      • -l 選項之後多出的資訊列

        • F: 核心分配給程式的系統標誌

        • S: 程式的狀態

          • O 代表正在執行
          • S 代表在休眠
          • R 代表可執行,正等待執行
          • Z 代表僵化,已終止但找不到其父程式
          • T 代表停止
        • PRI: 程式的優先順序(數字越大,優先順序越低)

        • NI: 謙讓度,用於決定優先順序

        • ADDR: 程式的記憶體地址

        • SZ: 程式被換出時所需交換空間的大致大小

        • WCHAN: 程式休眠的核心函式地址

    • BSD 風格選項,選項前不加連字元

      在使用 BSD 風格的選項時,ps命令會自動改變輸出以模仿 BSD 格式,上述很多輸出列跟使用 Unix 風格選項時是一樣的,但還是有一些不同之處

      • VSZ: 程式佔用的虛擬記憶體大小(以 KB 為單位)

      • RSS: 程式在未被交換出時佔用的實體記憶體大小

      • STAT: 代表當前程式狀態的多字元狀態碼

        程式狀態碼:第一個字元采用了與Unix風格的 S 輸出列 相同的值表明程式是在休眠、執行還是等待,第二個字元進一步說明了程式的狀態

        • <: 該程式以高優先順序執行
        • N: 該程式以低優先順序執行
        • L: 該程式有鎖定在記憶體中的頁面
        • s: 該程式是控制程式
        • l: 該程式擁有多執行緒
        • +: 該程式在前臺執行
    • GNU 長選項,選項前加雙連字元

      GNU 開發人員在經過改進的新ps命令中加入了另外一些選項,其中一些 GNU 長選項複製了現有的 Unix 或 BSD 風格選項的效果,而另外一些則提供了新功能

      • --forest 選項能夠使用 ASCII 字元來繪製圖表以顯示程式的層級資訊
  • top: 可以實時顯示程式資訊

    在top命令執行時鍵入可改變top的行為

    • 鍵入 f 允許你選擇用於對輸出進行排序的欄位
    • 鍵入 d 允許你修改 輪詢間隔 polling interval
    • 鍵入 q 可以退出 top

    利用該工具,可以輕易找出佔用系統大量資源的罪魁禍首

  • kill: 會向命令列中列出的所有 PID 傳送 TERM 訊號,TERM 訊號會告訴程式終止執行

    • 只能使用程式的 PID 而不能使用其對應的程式名
    • 要傳送程式訊號,必須是程式的屬主或 root 使用者
    • -s 選項支援指定其他訊號

    要檢查 kill 命令是否生效,可以再次執行 ps 命令或 top 命令,看看那些程式是否已經停止執行

  • pkill: 可以使用程式名代替 PID 來終止程式,允許使用萬用字元

    注意:以 root 身份使用命令中的萬用字元很容易意外地將系統的重要程式終止,這可能會導致檔案系統損壞

  • mount: 用於掛載儲存裝置,預設情況下會輸出當前系統已掛載的裝置列表

    命令提供了4部分資訊:

    • 裝置檔名
    • 裝置在虛擬目錄中的掛載點
    • 檔案系統型別
    • 已掛載裝置的訪問狀態

    手動掛載裝置的基本命令:

    mount -t type device directory
    
    • type: 磁碟格式化所使用的檔案系統型別

      Windows PC 共用移動儲存裝置,通常需要使用下列檔案系統型別

      • vfat: Windows FAT32檔案系統,支援長檔名
      • ntfs: Windows NT及後續作業系統中廣泛使用的高階檔案系統
      • exfat: 專門為可移動儲存裝置最佳化的Windows檔案系統
      • iso9660: 標準CD-ROM和DVD檔案系統

      大多數 U 盤會使用 vfat 檔案系統 格式化,如果需要掛載資料 CD 或 DVD,則必須使用 iso9660 檔案系統 型別

    • device: 該儲存裝置的裝置檔案位置

    • directory: 掛載點在虛擬目錄中的位置

    注意:儲存裝置被掛載到虛擬目錄,root 使用者就擁有了對該裝置的所有訪問許可權,而其他使用者的訪問則會被限制

  • umount: 移除可移動裝置時,不能直接將裝置拔下,應該先解除安裝

    命令的格式:

    umount [device | directory]
    

    支援透過 裝置檔案 或者 掛載點 來指定要解除安裝的裝置,如果有任何程式正在使用裝置上的檔案,則系統將不允許解除安裝該裝置

  • df: 檢視所有已掛載磁碟的使用情況

  • du: 可以顯示某個特定目錄(預設情況下是當前目錄)的磁碟使用情況

處理資料命令

  • sort: 對資料進行排序

    • -t 選項指定欄位分隔符
    • -k 選項指定排序欄位
    • -n 選項將數字按值排序
    • -M 選項將數字按月排序
  • grep: 會在輸入或指定檔案中逐行搜尋匹配指定模式的文字

  • gzip: 用於壓縮檔案

  • gzcat: 用於檢視壓縮過的文字檔案的內容

  • gunzip: 用於解壓檔案

Linux 基礎管理命令

使用者管理

  • useradd: 向 Linux 系統新增新使用者

    • 預設值使用 /etc/default/useradd 檔案設定
    • 安全設定在 /etc/login.defs 檔案中定義
    • 使用者賬戶管理命令需要以 root 使用者賬戶登入或者透過 sudo 命令執行
  • userdel: 從系統中刪除使用者

    • 預設情況下,userdel 命令只刪除 /etc/passwd 和 /etc/shadow 檔案中的使用者資訊,屬於該賬戶的檔案會被保留
    • -r 選項,則userdel會刪除使用者的 $HOME 目錄以及郵件目錄,然而系統中仍可能存有已刪除使用者的其他檔案
  • 修改使用者

    • usermod: 提供了修改 /etc/passwd 檔案中大部分欄位的相關選項只需指定相應的選項即可,大部分選項與useradd命令的選項一樣

      • -l: 修改使用者賬戶的登入名
      • -p: 修改賬戶密碼
      • -U: 解除鎖定,恢復使用者登入
      • -L: 可以鎖定賬戶,使使用者無法登入,無須刪除賬戶和使用者資料
      • -G: 提供向組中新增使用者不會影響主要組,更改了已登入系統的使用者所屬的組,則該使用者必須登出後重新登入,這樣新的組關係才能生效
      • -g: 則指定的組名會替換掉在 /etc/passwd 檔案中為該使用者分配的主要組
    • passwd: 可以方便地修改使用者密碼,只有 root 使用者才有許可權修改別人的密碼

    • chpasswd: 能從標準輸入自動讀取一系列以冒號分隔的登入名和密碼對偶

    • chfn: 提供了在 /etc/passwd 檔案的備註欄位中儲存資訊的標準方法,會將用於 Unix 的 finger 命令的資訊存入備註欄位

    • finger: 可以非常方便地檢視 Linux 系統的使用者資訊,安裝該命令可能會使你的系統受到攻擊漏洞的影響

    • chage: 命令可用於幫助管理使用者賬戶的有效期

  • groupadd: 可用於建立新組

  • groupdel: 刪除組

  • groupmod: 可以修改已有組

  • 檔案許可權:

    • 檔案許可權符號

      • r 代表物件是可讀的
      • w 代表物件是可寫的
      • x 代表物件是可執行的如果沒有某種許可權,則在該許可權位會出現連字元
    • 組許可權分別對應物件安全級別

      • 物件的屬主
      • 物件的屬組
      • 系統其他使用者
    • umask: 用來設定新建檔案和目錄的預設許可權

    • chmod: 可以修改檔案和目錄的安全設定,引數允許使用八進位制模式或符號模式來進行安全設定

    • chown: 前者可以修改檔案的屬主,可以修改檔案的所有符號連結檔案的所屬關係

      命令的格式

      chown options owner[.group] file
      
    • chgrp: 後者可以修改檔案的預設屬組

  • 訪問控制列表 ACL

    • getfacl: 能夠檢視分配給檔案或目錄的 ACL

    • setfacl: 能夠設定分配給檔案或目錄的 ACL

      • -m 選項修改分配給檔案或目錄的許可權
      • -x 選項刪除特定許可權

      3 種格式定義規則

      u[ser]:uid:perms
      g[roup]:gid:perms
      o[ther]::perms
      
      • 要為使用者分配許可權,可以使用 user 格式
      • 要為組分配許可權,可以使用 group 格式
      • 要為其他使用者分配許可權,可以使用 other 格式

管理檔案系統

  • fdisk: 可以在任何儲存裝置上建立和管理分割槽,但是隻能處理最大 2TB 的硬碟

    • 如果儲存裝置是首次分割槽,則會警告你該裝置沒有分割槽表
    • 是一個互動式程式,允許你輸入命令來逐步完成硬碟分割槽操作
    • 需要指定待分割槽的儲存裝置的名稱,同時還必須有超級使用者許可權
    • 不允許調整現有分割槽的大小,你能做的是刪除現有分割槽後重新建立
  • gdisk: 如果儲存裝置要採用 GUID 分割槽表 GUID partition table,GPT,就要用到

    • 會識別儲存裝置所採用的分割槽型別
    • 在轉換儲存裝置分割槽型別的時候務必小心,所選擇的型別必須與系統韌體相容
    • 提供了自己的命令列提示符,允許輸入命令進行分割槽操作
  • GNU parted: 操作命令偏向詞

    • 允許調整現有的分割槽大小,所以可以很容易地收縮或擴大磁碟分割槽

將資料儲存到分割槽之前,必須使用某種檔案系統對其進行格式化,並非所有的檔案系統工具都已經預設安裝過,要想知道某個工具是否可用,可以使用 type 命令

  • mkefs: ext
  • mke2fs: ext2
  • mkfs.ext3: ext3
  • mkfs.ext4: ext4
  • mkreiserfs: ReiserFS
  • jfs_mkfs: JFS
  • mkfs.xfs: XFS
  • mkfs.zfs: ZFS
  • mkfs.btrfs: Btrfs

為分割槽建立好檔案系統之後,下一步是將其掛載到虛擬目錄中的某個掛載點,以便在新分割槽中儲存資料

  • mount 命令會將新分割槽的檔案系統新增到掛載點
  • 掛載檔案系統的方法只能實現臨時掛載,重啟系統後就失效了,要強制 Linux 在啟動時自動掛載檔案系統可以將其新增到 /etc/fstab 檔案中

檔案系統的檢查與修復,每種檔案系統各自都有相應的恢復命令

  • fsck: 可以檢查和修復大部分Linux檔案系統型別

    • 日誌檔案系統的使用者確實也要用到 fsck 命令,但對於 COW 檔案系統需要高階修復選項
    • 只能對未掛載的檔案系統執行 fsck 命令,對大多數檔案系統只需先解除安裝檔案系統,檢查完成之後再重新掛載即可

LVM 管理

  • 物理卷 PV

    • pvscan: 掃描 PV
    • pvcreate: 指定了一個未使用的磁碟分割槽(或整個驅動器)由 LVM 使用,在這個過程中 LVM 結構、卷標和後設資料都會被新增到該分割槽
    • pvdisplay: 顯示 PV 資訊
    • pvremove: 刪除 PV
  • 卷組 VG

    • vgscan: 掃描 VG

    • vgcreate: 會將 物理卷 PV 加入儲存池,後者隨後用於構建各種邏輯卷

      • 可以存在多個卷組
      • 將一個或多個 PV 加入 卷組 VG 時,也會同時新增捲組的後設資料
      • 被指定為 PV 的分割槽只能屬於單個 VG,但被指定為 PV 的其他分割槽可以屬於其他 VG
    • vgdisplay: 顯示 VG 資訊

    • vgremove: 刪除 VG

    • vgextend: 擴充 VG

    • vgreduce: 縮小 VG

  • 邏輯卷 LV

    • lvscan: 掃描 LV

    • lvcreate: 邏輯卷 LV 由 VG 的 儲存空間塊 PE 組成

      • 可以使用檔案系統格式化 LV,然後將其掛載,像普通的磁碟分割槽那樣使用
      • 可以有多個 VG,但 LV 只能從一個指定的 VG 中建立
      • 多個 LV 可以共享單個 VG
    • lvdisplay: 顯示 LV 資訊,也可以使用 lvs 命令和 lvscan 命令顯示系統的 LV 資訊

    • lvremove: 刪除 LV

    • lvextend: 擴充 LV

    • lvreduce: 縮小 LV

要想了解所有的 LVM 命令,可以在命令列中輸入 lvm help

軟體包管理系統

  • 基於 Debian 的系統

    • dpkg: 是基於 Debian 的軟體包管理器的核心,用於在Linux系統中安裝、更新、刪除 DEB 包檔案

    • APT 工具集

      • apt-cache
      • apt-get
      • apt: 命令本質上是 apt-cache 命令和 apt-get 命令的前端

      apt 倉庫: 倉庫位置儲存在檔案 /etc/apt/sources.list 中

  • 基於 Red Hat 的系統

    • rpm: 是基於 Debian 的軟體包管理器的核心
    • yum: 用於 Red Hat, CentOS, Fedora
    • zypper: 用於 openSUSE
    • dnf: yum 的升級版,有一些新增的特性

    dnf 倉庫:

    • 配置檔案 /etc/dnf/dnf.conf
    • /etc/yum.repos.d 目錄中的單獨檔案

使用容器管理軟體

  • snap: 管理 snap 格式的應用程式容器

    • 在安裝 snap 的時候,snapd 程式會將其作為驅動器掛載
  • flatpak: 管理 flatpak 格式應用程式容器

理解 shell

  • shell 型別

    • 預設的互動式 shell default interactive shell 也稱 登入 shell login shell,只要使用者登入某個虛擬控制檯終端或是在 GUI 中啟動終端模擬器,該 shell 就會啟動
    • 預設的系統 shell default system shell/bin/sh 用於那些需要在啟動時使用的系統shell指令碼
  • $0 當前 shell 的名稱

  • exit 可以退出 shell

子 shell

  • 使用者登入某個 虛擬控制檯終端 或在 GUI 中執行 終端模擬器 時所啟動的預設的互動式 shell 之後,當 CLI 提示符處輸入 bash 命令(或是其他 shell 程式名)時會建立新的 shell 程式,這是一個 子 shell

    • 生成子程式時,只有部分父程式的環境被複制到了子環境中

    • bash 常用選項

      • -c string : 從 string 中讀取命令進行處理
      • -i : 啟動一個互動性 shell
      • -l : 做為 login shell
      • -r : 啟動一個受限 shell
      • -s : 從標準輸入讀取命令
  • 命令分組

    • 使用 () 圓括號程式列表,生成了一個子 shell 來執行這些命令
    • 使用 {} 花括號進行命令分組並不會像程式列表那樣建立子 shell
  • $BASH_SUBSHELL 變數判斷是否存在子 shell

    • 返回 0,那麼表明沒有子 shell
    • 返回大於 0 的數字,則表明存在子 shell
  • 子 shell 在 shell 指令碼中經常用於 多程式處理

  • 互動式 shell 中,一種高效的子 shell 用法是 後臺模式

    • 想將命令置入後臺模式,可以在命令末尾加上字元 &
    • 當其被置入後臺時,在 shell CLI 提示符返回之前,螢幕上會出現 後臺作業號程式 ID
    • jobs 命令能夠顯示當前執行在後臺模式中屬於你的所有程式
  • coproc: 建立協程同時做兩件事:

    • 在後臺生成一個子 shell
    • 在該子 shell 中執行命令
    • 除了會建立子 shell,協程基本上就是將命令置入後臺
  • 外部命令(有時也稱為檔案系統命令)是存在於bash shell之外的程式

    • 它並不屬於shell程式的一部分
    • 外部命令程式通常位於 /bin, /usr/bin, /sbin, /usr/sbin 目錄中
    • 每當執行外部命令時,就會建立一個子程式,這種操作稱為 衍生 forking
  • 內建命令無須使用子程式來執行

    • 已經和 shell 編譯成一體,作為 shell 的組成部分存在,無須藉助外部程式檔案來執行
    • type 命令來判斷某個命令是否為內建
  • history: 跟蹤你最近使用過的命令,是一個實用的內建命令,使用 !! 執行上一條命令

  • alias: 別名允許為常用命令及其引數建立另一個名稱,從而將輸入量減少到最低,另一個實用的shell內建命令

    • 選項 -p 可以檢視當前可用的別名
  • unalias: 刪除別名

環境變數

環境變數可以儲存 shell 會話和工作環境的相關資訊,允許在記憶體中儲存資料以便 shell 中執行的程式或指令碼能夠輕鬆訪問到這些資料

  • 全域性變數:全域性環境變數對於 shell 會話和所有生成的子 shell 都是可見的

    • 可以使用 env 命令來檢視全域性變數,使用 printenv 命令顯示個別環境變數的值
  • 區域性變數:只對建立它的 shell 可見

    • set 命令可以顯示特定程式的所有環境變數,既包括區域性變數、全域性變數
  • 引用某個環境變數時,必須在該變數名前加上美元符號 $

可以在 bash shell 中直接設定自己的變數

  • 可以使用等號為變數賦值實現 區域性環境變數,值可以是數值或字串
  • 在變數名、等號和值之間沒有空格,這一點非常重要
  • export 命令以及要匯出的變數名(不加 $ 符號)來實現 全域性環境變數
  • 修改子 shell 中的全域性環境變數並不會影響父 shell 中該變數的值
  • unset 命令能刪除已有的環境變數
  • 在子程式中刪除了一個全域性環境變數,那麼該操作 僅對子程式有效,該全域性環境變數在父程式中依然可用
  • 任何由父 shell 設定但 未匯出的變數都是區域性變數,不會被子 shell 繼承

環境變數的另一個特性是可以作為陣列使用

  • 環境變數的另一個特性是可以作為陣列使用
  • 要為某個環境變數設定多個值,可以把值放在 圓括號 中,值與值之間以 空格分隔
  • 要引用單個陣列元素,必須使用表示其在陣列中位置的 索引,索引要寫在 方括號 中,且 $ 符號 之後的所有內容都要放入 花括號
  • unset 命令可以刪除陣列中的某個值,後跟上陣列名來刪除整個陣列
  • 陣列並不太方便移植到其他 shell 環境,有時候陣列變數只會把事情搞得更復雜

預設的 shell 環境變數

  • CDPATH: 以冒號分隔的目錄列表,做為 cd 命令的搜尋路徑
  • HOME: 當前使用者主目錄
  • IFS: shell 用來將文字字串分割為字元
  • MAIL: 當前使用者收件箱的檔名
  • MAILPATH: 當前使用者收件箱的檔名列表
  • OPTARG: getop 命令處理的最後一個選項引數
  • OPTIND: getop 命令處理的最後一個選項引數的索引
  • PATH: shell 查詢命令的目錄列表,只需引用原來的 PATH 值新增冒號,然後再使用絕對路徑輸入新目錄,對於 PATH 變數的修改只能持續到退出或重啟系統
  • PS1: shell 命令列主提示符
  • PS2: shell 命令列次提示符
  • HISTFILESIZE: 歷史記錄列表上限,位於記憶體中
  • HISTSIZE: 歷史記錄檔案上限,位於硬碟上

當你登入 Linux 系統啟動 bash shell 時,預設情況下 bash 會在幾個檔案中查詢命令。這些檔案稱作 啟動檔案環境檔案

  • 登入 shell 通常會從 5 個不同的啟動檔案中讀取命令

    • /etc/profile

      • 是系統中預設的 bash shell 的主啟動檔案,系統中的 每個使用者 登入時都會執行這個啟動檔案
      • 每種發行版的 /etc/profile 檔案都有不同的設定和命令
    • $HOME/.bash_profile

      • 先檢查 $HOME 目錄中是不是還有一個名為 .bashrc 的啟動檔案,有就先執行該檔案中的命令
    • $HOME/.bashrc

      • 檢查 /etc 目錄下的通用 bashrc 檔案
      • 為使用者提供一個定製自己的命令別名
    • $HOME/.bash_login

    • $HOME/.profile

    $HOME 目錄下的啟動檔案:提供使用者專屬的啟動檔案來定義該使用者所用到的環境變數,Linux 發行版在環境檔案方面存在的差異非常大,有些使用者可能只有一個 $HOME/.bash_profile 檔案,順序 $HOME/.bash_profile -> $HOME/.bash_login -> $HOME/.profile 在 $HOME/.bashrc 檔案通常透過其他檔案執行

    • 作為互動式 shell 啟動的 bash 並不處理 /etc/profile 檔案,只檢查使用者 $HOME 目錄中的 .bashrc 檔案

    • 非互動式 shell,系統執行shell指令碼時用的就是這種 shell

      • bash shell 提供了 BASH_ENV 環境變數:當shell啟動一個 非互動式 shell 程式時,會檢查這個環境變數以檢視要執行的啟動檔名,如果有指定的檔案則 shell 會執行該檔案裡的命令,這通常包括 shell 指令碼變數設定
  • 有些 Linux 發行版使用了 可拆卸式認證模組 pluggable authentication module,PAM,這種情況下 PAM 檔案會在 bash shell 啟動之前被處理,前者中可能會包含環境變數

    • /etc/environment
    • $HOME/.pam_environment

環境變數持久化

  • 對全域性環境變數可能更傾向於將新的或修改過的變數設定放在 /etc/profile 檔案中,但升級了所用的發行版則該檔案也會隨之更新,好在 /etc/profile.d 目錄中建立一個以 .sh 結尾的檔案
  • 對儲存個人使用者永久性 bash shell 變數的最佳地點是 $HOME/.bashrc 檔案,但如果設定了 BASH_ENV 變數除非值為 $HOME/.bashrc,否則應該將 非互動式 shell 的使用者變數放在別的地方

構建 shell 指令碼

基本使用

  • 使用多個命令,彼此用 分號 隔開

  • 建立 shell 指令碼檔案

    • 建立 shell 指令碼檔案時,必須在檔案的第一行指定要使用的 shell

      #!/bin/bash
      
      • 第一行有時被稱為 shebang

      基本是 #! 加 shell 絕對路徑

    • 註釋使用 #

    • 使用分號將兩個命令放在一行中,但在shell指令碼中,可以將命令放在獨立的行中

  • 使用 shell 指令碼:需要 可執行許可權下面查詢命令規則之一

    • 將放置 shell 指令碼檔案的目錄新增到 PATH 環境變數中
    • 在命令列中使用絕對路徑或相對路徑來引用 shell 指令碼檔案
  • echo: 輸出會顯示在指令碼所執行的控制檯顯示器,可用單引號或雙引號來劃定字串

  • 變數使用

    • 在指令碼中,可以在環境變數名之前加上 $ 來引用這些環境變數
    • 反斜線允許 shell 指令碼按照字面意義解釋 $
    • 透過 ${variable} 形式引用的變數,花括號 通常用於幫助界定 $ 後的變數名
    • 變數賦值在變數、等號和值之間不能出現空格
    • 引用變數值時要加 $,對變數賦值時則不用加 $
  • 命令替換

    • 可以從命令輸出中提取資訊並將其賦給變數

    • 兩種方法可以將命令輸出賦給變數

      • 反引號
      • $()
    • 命令替換允許將 shell 命令的輸出賦給變數

    • 命令替換會建立出子 shell 來執行指定命令,這是由執行指令碼的 shell 所生成的一個獨立的 shell,在子 shell 中執行的命令無法使用指令碼中的變數

  • 輸出重定向 >

    • 最基本的重定向會將命令的輸出傳送至檔案
    • 如果輸出檔案已存在,則重定向運算子會用新資料覆蓋已有的檔案
    • 不想覆蓋檔案原有內容,使用 >>
  • 輸入重定向 <

    • 輸入重定向會將檔案的內容重定向至命令
    • 還有另外一種輸入重定向的方法稱為 內聯輸入重定向 <<,這種方法無須使用檔案進行重定向,只需在命令列中指定用於輸入重定向的資料即可
    • 除了 << 符號,必須指定一個 文字標記 來劃分輸入資料的起止
  • 管道 |

    • 將一個命令的輸出作為另一個命令的輸入
    • 管道可以串聯的命令數量沒有限制
  • 執行數學運算

    • expr: 最初,Bourne shell 提供了一個專門用於處理數學表示式的命令

      • 可在命令列中執行數學運算,但是特別笨拙
      • 能夠識別少量算術運算子和字串運算子
      • 標準運算子在 expr 命令中工作得很好,但在指令碼或命令列中使用時仍有問題出現
      • 那些容易被 shell 錯誤解釋的字元被傳入 expr 命令之前,需要使用 跳脫字元 對其進行轉義
      • 為了相容 Bourne shell,bash shell 保留了 expr 命令
    • 使用方括號

      • 在 bash 中,要將數學運算結果賦給變數,可以使用 $方括號
      • 在使用方括號執行數學運算時,無須擔心 shell 會誤解乘號或其他符號
      • bash shell 的數學運算子只支援整數運算,但 zsh 提供了完整的浮點數操作
    • 使用內建的 bash 計算器 bc

      • 其中內建變數 scale 控制冗長
      • 在指令碼中需要結合管道使用,允許你設定變數,如果需要多個變數可以用分號來分隔它們
      • 表示式中不僅可以使用數字,還可以用shell指令碼中定義好的變數
      • 這種方法適用於較短的運算,如果要進行大量運算,最好的辦法是使用內聯輸入重定向
  • 變數 $? 來儲存最後一個已執行命令的退出狀態碼

    • 對於成功結束的命令,其退出狀態碼是 0
    • 對於因錯誤而結束的命令,其退出狀態碼是一個正整數
    • 退出狀態碼被縮減到了 0~255 的區間

結構化命令

  • if-then-elif-then-else

    if command1
    then
        commands
    elif command2
    then
        commands
    else
        commands
    fi
    
    • if 或 elif 根據命令的退出狀態碼判斷 then 中的命令是否執行

      • 退出狀態碼為 0 時執行 then 部分的命令
      • 退出狀態碼為非 0 退出狀態碼時,執行 else 中程式碼
    • then 與 if, elif 是配套

    • fi 為閉合開始的 if

    • elif 和 else 為可選

  • test: 測試命令,目的是更好的進行條件判斷

    • 如果 test 命令中列出的條件成立,那麼 test 命令就會退出並返回退出狀態碼 0
    • 如果條件不成立,那麼 test 命令就會退出並返回非 0 的退出狀態碼
    test condition
    
    • condition 要測試的一系列引數和值
    • condition 部分沒有會以非 0 的退出狀態碼

    bash shell 提供了另一種條件測試方式,可以使用 中括號 替代 test,第一個方括號之後和第二個方括號之前 必須留有空格

    test命令和測試條件可以判斷 3 類條件:

    • 數值比較

      使用 -eq, -ge, -gt, -le, -lt, -ne 替代數學中的比較運算子 ==, >=, >, <=, <, != 下面是記憶

      • e 與等值相關
      • g 與大於相關
      • l 與小於相關
      • n 有否相關
      • q, t 基本比較
    • 字串比較

      • = 比較字串是否相同

      • != 比較字串是否相同

      • <, > 比較兩個字串大小,使用時必須轉義

        • 在比較的時候使用的是每個字元的 Unicode 編碼值
        • sort 命令處理大寫字母的方法剛好與 test 命令相反,比較測試中大寫字母被認為是小於小寫字母的
      • -n 判斷字串長度是否不為 0

      • -z 判斷字串長度是否為 0

    • 檔案比較

      • -e 是否存在
      • -s 是否存在且非空
      • -d 是否為目錄
      • -f 是否為檔案
      • -r 是否可讀
      • -w 是否可寫
      • -x 是否可執行
      • -O 是否當前使用者是檔案屬主
      • -G 是否當前使用者組
      • 兩個檔案比較新舊,測試之前務必確保檔案存在
        • -nt 是否前者新,new time
        • -ot 是否前者舊,old time
  • 複合條件測試

    • && 與運算
    • || 或運算
  • bash shell 在 if 語句中的高階特性

    • 在子 shell 中執行命令的單括號

      • test 語句中使用程式列表時,可能會出現意料之外的結果
    • 用於數學表示式的雙括號

      • 雙括號命令允許在比較過程中使用 高階數學表示式,任意的數學賦值或比較表示式
      • test 命令在進行比較的時候只能使用簡單的算術操作
      • 雙括號中表示式的不用轉義處理
    • 用於高階字串處理功能的雙方括號

      • 使用雙等號進行模式匹配,右邊定義匹配的表示式,支援萬用字元或正規表示式
      • 不是所有的 shell 都支援雙方括號
  • case: 比較變數尋找特定的值

    case variable in
    pattern1 | pattern2) commands1;;
    pattern3) commands2;;
    *) commands3;;
    esac
    
    • 將指定變數與不同模式進行比較
    • 豎線運算子在一行中分隔出多個模式
    • 星號會捕獲所有與已知模式不匹配的值
    • esac 進行閉合 case
  • for: 迴圈處理

    for var in list
    do
        commands
    done
    
    • list 是迭代列表,每次迭代中變數 var 會包含列表中的當前值

    • list 中值之間是以 空格 分隔的

      -環境變數 內部欄位分隔符 IFS可以關閉分隔規則

      • 需要修改 IFS 的值時,注意將其恢復原狀
      • 一種安全的做法是在修改IFS之前儲存原來的IFS值,之後再恢復它
      • 指定多個 IFS 字元,則只需在賦值語句中將這些字元寫在一起即可
    • 變數包含了用於迭代的值列表可以用於迭代列表,值列表中能追加或者拼接

    • do-done 中為迴圈體

    • 最後一次迭代結束後,變數 var 的值在 shell 指令碼的剩餘部分依然有效

    • list 中複雜的資料處理

      • 使用跳脫字元
      • 使用雙引號來劃分值
    • 從命令中讀取值列表

    • 使用萬用字元讀取目錄

      • 此時變數 var 放入雙引號內,目錄名和檔名中包含 空格 是完全合法的
      • 允許列出多個目錄萬用字元
      • 即使檔案或目錄不存在,for 語句也會嘗試把列表處理完,最好在處理之前先測試一下檔案或目錄
    • 支援仿 C 語言風格的 for 命令,但注意是使用 (()) 而不是 C 語言的 (),有些地方與bash shell 標準的 for 命令並不一致

      • 變數賦值可以有空格
      • 迭代條件中的變數不以美元符號開頭
      • 迭代過程的算式不使用expr命令格式

      因此,在指令碼中使用仿 C 語言的 for 迴圈時要小心

  • while: 某種程度上糅合了 if 語句和 for 迴圈

    該命令返回的退出狀態碼為0,就迴圈執行一組命令

    while test command
    do
        commands
    done
    
    • 判斷部分類似 if
    • 迴圈體使用與 for 相同
    • 修改測試條件中用到的變數,否則就會陷入死迴圈
    • 允許在 while 語句行定義多個測試命令,但只有最後一個測試命令的退出狀態碼會被用於決定是否結束迴圈
    • 支援巢狀
  • until: 與 while 命令工作的方式完全相反,注意測試部分是反的即可

  • 迴圈控制

    • break: 退出迴圈,後面可以指定數字,數字是要跳出的迴圈層級,預設 1
    • continue: 提前中止某次迴圈,也允許透過命令列引數指定要繼續執行哪一級迴圈
  • 處理迴圈的輸出

    • 對迴圈的輸出使用管道或進行重定向,這可以透過在 done 命令之後新增一個處理命令來實現

處理輸入輸出

  • 傳遞引數: 向 shell 指令碼傳遞資料的最基本方法是使用命令列引數,命令列引數允許執行指令碼時在命令列中新增資料

    • 引數之間是以空格分隔的

    • bash shell 會將所有的命令列引數都指派給 位置變數

      • 位置變數的名稱都是標準數字
      • $0 對應指令碼名,$1 對應第一個命令列引數,以此類推直到 $9
      • 在超過 9 個引數之後,必須在變數名兩側加上花括號,比如 ${10}
      • 執行指令碼時使用的是絕對路徑,那麼位置變數 $0 就會包含整個路徑
      • basename: 只要是用於去除路徑和檔案字尾部分的檔名或者目錄名
    • 在使用位置變數之前一定要檢查是否為空

  • 特殊引數變數

    • $# 含有指令碼執行時攜帶的命令列引數的個數
    • 那麼變數 ${$#} 應該就代表了最後一個位置變數,不能在花括號內使用 $,必須將 $ 換成 !
    • $* 變數會將所有的命令列引數視為一個單詞,變數會將這些引數視為一個整體
    • $@ 變數會將所有的命令列引數視為同一字串中的多個獨立的單詞,以便你能遍歷並處理全部引數
    • $$ 當前 PID
  • shift: 移動引數

    • 會根據命令列引數的相對位置進行移動
    • 預設情況下會將每個位置的變數值都向左移動一個位置
    • 變數 $1 的值則會被刪除,變數 $0 的值不會改變
    • 如果某個引數被移出,那麼它的值就被丟棄了無法再恢復
    • 也可以一次性移動多個位置,指明要移動的位置數即可
  • 處理選項

    • 提取單個引數時,使用 case 語句
    • 在Linux中這個特殊字元是 雙連字元 --,shell 會用雙連字元表明選項部分結束
    • 選項佔用了兩個位置,所以還需要使用shift命令多移動一次

    getopt: 能夠識別命令列引數,簡化解析過程,將命令列中選項和引數處理後只生成一個輸出

    getopt optstring parameters
    
    • optstring:

      • 定義了有效的命令列選項字母以及是否需要引數值
      • 需要引數值的選項字母后面加一個 冒號
      • 未包含你指定的選項,則在預設情況下,getopt 命令會產生一條錯誤訊息,使用 -q 可以忽略
    • parameters: 引數列表

    set: 有一個選項是 雙連字元 --,可以將 位置變數 的值替換成 set 命令所指定的值

    set -- $(getopt optstring "$@")
    
    • optstring: 是你設計的命令列選項
    • getopt 命令並不擅長處理 帶空格和引號的引數值,它會將空格當作引數分隔符

    getopts: 是 bash shell 的內建命令,比 getopt 多了一些擴充套件功能,能夠和已有的 shell 位置變數配合默契

    getopts [:]optstring variable
    
    • getopts 每次只處理一個檢測到的命令列引數

    • 在處理完所有的引數後,getopts 會退出並返回一個大於 0 的退出狀態碼,適合用在解析命令列引數的迴圈中

      • : 為可選,類似 getopt 命令 -p 引數,有則不顯示錯誤訊息
      • optstring 值與 getopt 命令中使用的值類似
      • variable 每次處理時儲存它們的變數名
    • getopts 涉及兩個環境變數

      • OPTARG 環境變數儲存帶參選項的引數值
      • OPTIND 環境變數儲存著引數列表中正在處理的引數位置
    • getopts 命令會移除起始的 連字元,所以在 case 語句中不用連字元

    • 可以在引數值中加入空格判斷引號界限,能將選項字母和引數值寫在一起,在兩者之間不加空格

    • 還可以將在命令列中找到的所有 未定義的選項 統一輸出成 問號

    • 知道何時停止處理選項,並將引數留給你處理,處理每個選項時,getopts 會將 OPTIND 環境變數值增 1,可以使用 shift 命令和 OPTIND 值來移動引數

  • 獲取使用者輸入

    • read: 從標準輸入或另一個檔案描述符中接受輸入

      • 獲取輸入後,read 命令會將資料存入變數

      • 如果指定多個變數,則輸入的每個資料值都會分配給變數列表中的下一個變數

      • 如果變數數量不夠,那麼剩下的資料就全部分配給最後一個變數

      • 不指定任何變數,這會將接收到的 所有資料 都放進特殊環境變數 REPLY

      • -p 選項,允許直接指定提示符

      • -t 選項,指定一個計時器判斷是否輸入超時,單位秒

      • -n 選項,統計輸入的字元數,當字元數達到預設值時,就自動退出

      • -s 選項,避免在輸入的資料出現在螢幕上

      讀取檔案

      • 從指定檔案中讀取一行文字,當檔案中沒有內容可讀時,會退出並返回非 0 退出狀態碼
  • 標準檔案描述符:Linux 系統會將每個物件當作檔案來處理,這包括輸入和輸出

    • 檔案描述符是一個非負整數,唯一會標識的是會話中開啟的檔案

      • 0: STDIN 檔案描述符代表 shell 的標準輸入
      • 1: STDOUT 檔案描述符代表 shell 的標準輸出
      • 2: STDERR 檔案描述符處理錯誤訊息
    • 每個程式一次最多可以開啟 9 個檔案描述符

    • 在預設情況下,STDERR 和 STDOUT 指向同一個地方

    • STDERR 並不會隨著 STDOUT 的重定向發生改變

    • 可以將 檔案描述符 索引值放在重定向符號之前,只重定向對應資訊,兩者必須緊挨著

      • 1> 輸出重定向標準輸出
      • 2> 輸出重定向錯誤訊息
    • bash shell 提供特殊的重定向符 &> 將 STDERR 和 STDOUT 的輸出重定向

  • 在指令碼中重定向輸出

    • 臨時重定向 &

      • 在重定向到檔案描述符時,必須在檔案描述符索引值之前加一個 &
      • 非常適合在指令碼中生成錯誤訊息
    • 永久重定向 exec

      • 在指令碼執行期間重定向某個特定檔案描述符
      • exec 會啟動一個新 shell
      • 適合指令碼中有大量資料需要重定向
      • 允許將 STDIN 重定向為檔案
  • 替代性檔案描述符

    • 替代性檔案描述符從 3~8 共6個,均可用作輸入或輸出重定向,任意一個都可以分配給檔案並用在指令碼中
    • 使用 exec 將替代性檔案描述符指向檔案,此重定向就會一直有效,直至重新分配
    • 恢復已重定向的檔案描述符,你可以將另一個檔案描述符分配給標準檔案描述符
    • 可以開啟單個檔案描述符兼做輸入和輸出,這樣就能用同一個檔案描述符對檔案進行讀和寫兩種操作,任何讀或寫都會從檔案指標上次的位置開始
  • 關閉檔案描述符

    • 如果建立了新的輸入檔案描述符或輸出檔案描述符,那麼 shell 會在指令碼退出時自動將其關閉
    • 手動關閉檔案描述符,只需將其重定向到特殊符號 &-
    • 一旦關閉了檔案描述符,就不能在指令碼中向其寫入任何資料,否則 shell 會發出錯誤訊息
  • lsof: 會列出整個 Linux 系統開啟的所有檔案描述符

    • -p 允許指定 PID
    • -d 允許指定要顯示的檔案描述符編號
    • -a 可用於對另外兩個選項的結果執行 AND 運算
  • 抑制命令輸出

    • 重定向到一個名為 null 檔案的特殊檔案
    • 輸出到 null 檔案的任何資料都不會被儲存,全部會被丟棄
    • null檔案的標準位置是 /dev/null
    • 輸入重定向中將 /dev/null,實現快速清除現有檔案中的資料
  • 使用臨時檔案

    • Linux 系統有一個專供臨時檔案使用的 特殊目錄 /tmp

    • 大多數 Linux 發行版配置系統在啟動時會自動刪除 /tmp 目錄的所有檔案

    • 系統中的任何使用者都有許可權讀寫 /tmp 目錄中的檔案

    • mktemp: 專門用於建立臨時檔案

      • 所建立的臨時檔案不使用預設的 umask 值
      • 作為臨時檔案屬主,你擁有該檔案的讀寫許可權,但其他使用者無法訪問
      • 使用方法只需指定一個檔名模板即可,同時在檔名末尾要加上 6 個 X
      • 命令會任意地將 6 個 X 替換為同等數量的字元,以保證檔名在目錄中是唯一的
      • 命令的輸出正是它所建立的檔名,方便在指令碼中使用
      • -t 選項會強制在系統的臨時目錄中建立檔案,返回所建立的臨時檔案的完整路徑名
      • -d 選項會建立一個臨時目錄
  • 記錄訊息

    • tee

      • 就像是連線管道的 T 型接頭,它能將來自 STDIN 的資料同時送往兩處

        • STDOUT
        • 命令列所指定的檔名
      • 預設情況下,會在每次使用時覆蓋指定檔案的原先內容

      • -a 選項: 將資料追加到指定檔案中

指令碼控制

  • 處理訊號

    • Linux 系統和應用程式可以產生超過 30 個訊號,訊號與值在不同版本可能會存在差異,可以透過 kill-l 選項檢視

    • bash shell 會忽略收到的任何 SIGQUIT 訊號和 SIGTERM 訊號,保障互動式 shell 不會被意外終止

      • SIGQUIT 訊號 3: 停止程式
      • SIGTERM 訊號 15: 儘可能的終止程式,不一定成功比較溫和
    • bash shell 會處理收到的所有 SIGHUP 訊號和 SIGINT 訊號

      • SIGHUP 訊號 1: 掛起程式
      • SIGINT 訊號 2: 中斷程式,Linux 核心將不再為 shell 分配 CPU 處理時間
    • 產生訊號: bash shell 允許使用鍵盤上的組合鍵來生成兩種基本的 Linux 訊號

      • Ctrl+C 組合鍵會生成 SIGINT 訊號

      • Ctrl+Z 組合鍵會生成 SIGTSTP 訊號

        • SIGTSTP 訊號 20: 停止 shell 中執行的任何程式,還能從上次停止的位置繼續執行,可以使用 kill 傳送資訊 SIGKILL 訊號或 SIGCONT 訊號進行控制
        • SIGKILL 訊號 9: 強制終止程式
        • SIGCONT 訊號 18: 在 SIGSTOP, SIGTSTP 後恢復
        • 用 ps 命令可以檢視已停止的程式,在 S 列停止狀態顯示為 T
    • 捕獲訊號

      trap 命令可以指定 shell 指令碼需要偵測並攔截的 Linux 訊號

      trap commands signals
      
      • commands 部分列出想要與訊號繫結的行為,如果是 -- 會恢復訊號的預設行為

      • signals 部分列出想要捕獲的訊號,多個訊號之間以空格分隔,可以使用訊號的值或訊號名

      • 為了保證指令碼中的關鍵操作不被打斷,請使用帶有空操作命令的 trap 以及要捕獲的訊號列表

      • 要捕獲 shell 指令碼的退出,只需在 trap 命令後加上 EXIT 訊號,提前退出指令碼依然能捕獲

  • 後臺模式執行

    • 在後臺模式中,程式執行時不和終端會話的 STDIN, STDOUT, STDERR 關聯
    • 指令碼在後臺執行,不佔用終端會話
    • 後臺模式執行shell指令碼只需在指令碼名後面加上 &
    • 當後臺程式執行時仍然會使用 終端顯示器 來顯示 STDOUT 和 STDERR 訊息,最好是進行重定向避免這種雜亂的輸出
  • 在非控制檯下執行指令碼

    • 即便退出了終端會話,也在終端會話讓指令碼一直以後臺模式執行到結束

    • nohup: 能阻斷髮給特定程式的 SIGHUP 訊號,當退出終端會話時可以避免程式退出

      • 命令會解除終端與程式之間的關聯,因此程式不再同 STDOUT 和 STDERR 繫結在一起
      • 命令會自動將 STDOUT 和 STDERR 產生的訊息重定向到一個名為 nohup.out 的檔案中
      • nohup.out 檔案一般在 當前工作目錄 中建立,否則會在 $HOME 目錄 中建立
      • 執行了另一個命令,那麼該命令的輸出會被追加到已有的 nohup.out 檔案中
  • 作業控制: 包括啟動、停止、終止、恢復

    • jobs: 作業控制命令

      • 命令輸出中的加號和減號

        • 帶有加號的作業為預設作業,如果作業控制命令沒有指定作業號,則引用的就是該作業
        • 帶有減號的作業會在預設作業結束之後成為下一個預設作業
        • 帶加號的作業只能有一個,帶減號的作業也只能有一個
      • -l 選項: 檢視作業的 PID

    • 刪除已停止的作業,那麼使用 kill 命令向其 PID 傳送 SIGKILL 訊號即可

    • bg: 以後臺模式重啟作業,存在多個作業需要在後加上作業號

    • fg: 以前臺模式重啟作業

  • 調整謙讓度

    • 排程優先順序是指核心為程式分配的 CPU 時間

    • shell 啟動的所有程式的排程優先順序預設都是相同的

    • 排程優先順序是一個整數值,取值範圍從-20(最高優先順序)到+19(最低優先順序)

    • 在預設情況下,bash shell 以優先順序 0 來啟動所有程式

    • nice: 允許在啟動命令時設定其排程優先順序

      • 命令會阻止普通使用者提高命令的優先順序,只有 root 使用者或者特權使用者才能提高作業的優先順序
    • renice: 指定已執行程式的 PID 來改變其優先順序

      • 只能對屬主為自己的程式使用 renice 且只能降低排程優先順序
      • root 使用者和特權使用者可以使用任意程式的優先順序做任意調整

定時執行作業

  • at: 允許指定Linux系統何時執行指令碼

    • at 的守護程式 atd 在後臺執行,在作業佇列中檢查待執行的作業

    • atd 守護程式會檢查系統的一個特殊目錄,通常位於 /var/spool/at 或 /var/spool/cron/atjobs

    • 預設情況下,atd 守護程式每隔 60 秒檢查一次這個目錄

    • 在預設情況下,命令會將 STDIN 的輸入放入佇列

    • -f 選項: 指定用於從中讀取命令

    • 命令能識別多種時間格式,具體參見 /usr/share/doc/at/timespec 檔案

    • 使用命令時,該作業會被提交至 作業佇列,針對不同優先順序有 52 種作業佇列

      • 作業佇列的字母排序越高,此佇列中的作業執行優先順序就越低
      • 預設情況下,提交的作業會被放入 a 佇列
    • -q 選項: 指定其他的佇列

    • 任何送往 STDOUT 或 STDERR 的輸出都會透過 郵件系統 傳給該使用者,最好在指令碼中進行重定向

    • -M 選項: 以禁止作業產生的輸出資訊

  • atq: 可以檢視系統中有哪些作業在等待

  • atrm: 刪除等待中的作業,指定要刪除的作業號即可

  • cron: 程式排程需要定期執行的作業,相比 at 具有周期性

    • 在後臺執行,並會檢查一個特殊的表(時間表),從中獲知已安排執行的作業
    • 時間表格式: minutepasthour hourofday dayofmonth month dayofweek command
    • 時間表允許使用特定值、取值範圍或者萬用字元來指定各個欄位
    • 命令列表必須指定要執行的命令或指令碼的完整路徑
    • 會以提交作業的使用者身份執行該指令碼,因此你必須有訪問該指令碼以及輸出檔案的合理許可權
    • 每個使用者都可以使用自己的 cron 時間表執行已安排好的任務
    • 在預設情況下,使用者的 cron 時間表檔案並不存在
  • anacron: 彌補 Linux 系統處於關閉狀態時,cron 程式不會再去執行那些錯過的作業

    • anacron 判斷出某個作業錯過了設定的執行時間,它會盡快執行該作業
    • 只處理位於 cron 目錄的程式
    • 它透過時間戳來判斷作業是否在正確的計劃間隔內執行了,每個 cron 目錄都有一個時間戳檔案,該檔案位於 /var/spool/anacron
    • 命令使用自己的時間表(通常位於 /etc/anacrontab)來檢查作業目錄

    anacron 時間表的基本格式: period delay identifier command

    • period: 定義了作業的執行頻率,單位 day
    • delay: 指定了在系統啟動後 anacron 程式需要等待多少分鐘再開始執行錯過的指令碼
    • 不會執行位於 /etc/cron.hourly 目錄的指令碼,因為命令不處理執行時間需求少於一天的指令碼
    • identifier: 是一個獨特的非空字串,作用是標識出現在日誌訊息和錯誤 email 中的作業
    • command: 包含了 run-parts 程式和一個 cron 指令碼目錄名
  • 啟動 shell 時執行指令碼

    • 應該將需要在登入時執行的指令碼放在 $HOME/.bash_profile

    • 如果需要某個指令碼在兩個時刻都執行可以將其放入 .bashrc

      • 一次是當使用者登入 bash shell 時
      • 另一次是當使用者啟動 bash shell 時
  • source: 這是另一種執行 bash 指令碼的方法,稱為 源引

shell 函式

  • bash shell 提供的使用者自定義函式功能

  • 建立函式

    • 使用關鍵字 function

      function name{
          commands
      }
      
      • 函式名稱唯一,指令碼中的函式名不能重複
      • 如果定義了同名函式,那麼新定義就會覆蓋函式原先的定義
    • bash shell 指令碼中定義函式的方式建立函式

      name(){
          commands
      }
      
      • 函式名後的空括號表明正在定義的是一個函式,這種語法的命名規則和第一種語法一樣
  • 呼叫函式

    • 只需像其他 shell 命令一樣寫出函式名

    • 函式可以視為一個小型指令碼,執行結束時會返回一個退出狀態碼

    • 函式的退出狀態碼是函式中最後一個命令返回的退出狀態碼

      • $? 可以確定函式的退出狀態碼,提取函式返回值之前執行了其他命令,那麼函式的返回值會丟失

      • return: 以特定的退出狀態碼退出函式

        • 函式執行一結束就立刻讀取返回值
        • 退出狀態碼必須介於 0~255
    • 可以將命令的輸出儲存到 shell 變數中一樣,也可以將函式的 STDOUT 輸出儲存到 shell 變數中

    • 函式可以使用 標準的位置變數 來表示在命令列中傳給函式的任何引數

      • $0 變數儲存函式名

      • 函式引數依次儲存在 $1, $2 等變數中

      • $# 可以確定傳給函式的引數數量

      • 要在函式中使用指令碼的命令列引數,必須在呼叫函式時手動將其傳入

      • 向函式傳遞陣列

        • 試圖將陣列變數作為函式引數進行傳遞,則函式只會提取陣列變數的第一個元素
        • 須先將陣列變數拆解成多個陣列元素,然後將這些陣列元素作為函式引數傳遞,返回陣列變數也採用類似的方法
  • 變數的作用域

    • 全域性變數

      • 在 shell 指令碼內任何地方都有效的變數
      • 預設情況下,在指令碼中定義的任何變數都是全域性變數
      • 在函式外定義的變數可在函式內正常訪問
    • 區域性變數

      • 無須在函式中使用全域性變數,任何在函式內部使用的變數都可以被宣告為區域性變數
      • 變數宣告之前加上 local 關鍵字即可,保證了變數僅在該函式中有效,可以輕鬆地將函式變數和指令碼變數分離開
  • 函式遞迴

    • 函式可以呼叫自己來得到結果
    • 透過遞迴對複雜的方程進行逐級規約,直到基準值
  • 建立庫

    • bash shell 允許建立函式庫檔案,然後在多個指令碼中引用此庫檔案
    • source: 會在當前shell的上下文中執行命令,而不是建立新的shell並在其中執行命令,這樣指令碼就可以使用庫中的函式
    • source命令有個別名,稱作 點號運算子 .
    • 在 .bashrc 檔案中定義函式,可長期在命令列復用函式,只需將函式放在檔案末尾即可,也可以源引庫檔案
    • GNU shtool shell 指令碼函式庫,提供了一些簡單的 shell 指令碼函式,可用於實現日常的 shell 功能

shell 指令碼高階技巧

sed & gawk

  • sed 編輯器

    • 被稱作 流編輯器,根據事先設計好的一組規則編輯資料流

    • 可以執行下列操作

      • 從輸入中讀取 一行 資料
      • 根據所提供的編輯器命令 匹配資料
      • 按照命令 修改 資料流中的資料
      • 將新的資料輸出到 STDOUT,編輯器並不會修改文字檔案的資料
    • 在流編輯器匹配並針對一行資料執行所有命令之後,會重複這個過程直到處理完資料流後結束執行

    • 命令的格式

      sed options script file
      
      • options

        • -e 選項額外 sed 命令,執行多個命令

          • 兩個命令都應用於檔案的每一行資料,命令之間必須以 分號 分隔
          • 命令末尾和分號之間不能出現 空格
        • -f 選項在單獨的檔案中指定 sed 命令,目的是大量要執行時使用

          • 指定檔案中一條命令應於檔案每一行
          • .sed 作為 sed 指令碼檔案的副檔名,便於識別
        • -n 選項會抑制 sed 編輯器的輸出

      • script: 指定了應用於流資料中的單個命令

    • 預設情況下,會將指定的命令應用於 STDIN 輸入流中,可以直接將資料透過管道傳入

    • sed 命令

      • 替換命令 s: [address]s/替換目標/替換內容/flags

        替換標識

        • 數字: 指明新文字將替換行中的 第幾處匹配
        • g: 指明新文字將替換行中 所有的匹配
        • p: 指明列印出替換後的行
        • w file: 將替換的結果寫入檔案
        • sed 編輯器允許選擇其他字元作為替換命令的替代分隔符,/ 不是絕對的

        行定址 address

        • 數字模式

          • n: 表示特定行,$ 識別符號表示最後一行
          • n,m: 表示 n 行到 m 行的範圍
        • 正規表示式模式

          • /pattern/command: pattern 匹配表示式
        • 可以對特定地址的多個命令分組

          address {
              sed commands
          }
          
      • 刪除命令 d: 後面通常不接任何

        [address]d
        
      • 插入命令 i: 會在指定行前增加一行,每行新文字末尾使用反斜線 \

        [address]i\
        strings\
        ...\
        strings
        
      • 附加命令 a: 會在指定行後增加一行,每行新文字末尾使用反斜線 \

        [address]a\
        strings\
        ...\
        strings
        
      • 取代命令 c: 修改行,將範圍內取代內容,它跟插入和附加命令的工作機制一樣

        [address]c\
        strings\
        ...\
        strings
        
      • 轉換命令 y: 唯一可以處理單個字元,inchars 和 outchars 進行一對一的對映

        [address]y/inchars/outchars
        
      • 寫入命令 w: 向檔案寫入行

        [address]w filename
        
      • 讀取命令 w: 將一條獨立檔案中的資料插入資料流

        [address]r filename
        
      • 命令 F: 告知 sed 列印出當前正在處理的檔名

        [address]F
        

      所以命令相同部分 [address]command

    • 列印

      • p 命令: 列印文字行
      • = 命令: 列印行號
      • l 命令: 可以列印資料流中的文字和不可列印字元,行尾的美元符號表示換行符,
  • gawk 編輯器

    • 相比 sed 增加了一種程式語言,而不僅僅是編輯器命令

      • 定義變數 來儲存資料
      • 使用算術和字串 運算子 來處理資料
      • 使用 結構化程式設計概念 為資料處理新增處理邏輯
      • 提取檔案中的資料將其 重新排列組合,最後生成 格式化 報告
    • 命令的格式

      gawk options program file
      
      • options

        • -F 指定行中分隔符
        • -f 從指令碼檔案中讀取 gawk 命令,gawk 指令碼建議以 .gawk 為字尾
        • -v 定義變數
        • -L 指定相容模式或警告級別
      • program: gawk 指令碼

      • file: 處理資料,沒有會從 STDIN 接收資料

    • gawk 指令碼用一對花括號來定義

      • print: 會將文字列印到 STDOUT
      • STDIN 接入資料,會會反覆直到 EOF 字元為止,EOF 字元表示檔案末尾
      • Ctrl+D 組合鍵可以生成 EOF 字元
    • 特性之一是會自動為每一行的各個資料元素分配一個變數

      • $0 代表整個文字行
      • $1 代表文字行中的第一個資料欄位,其中 $2, $3, ..., $n 以此內推
      • 文字行中的 資料欄位 是透過 欄位分隔符 來劃分的
      • 預設情況下,欄位分隔符是任意的 空白字元
    • BEGIN: 會強制 gawk 在讀取資料前執行 BEGIN 關鍵字之後指定的指令碼

    • END: 允許指定一段指令碼在 gawk 處理完資料後執行這段指令碼

    • 特殊變數 FS: 這是定義欄位分隔符的另一種方法

  • sed, gawk 職能

    • sed 更適合編輯匹配到的文字
    • gawk 更適合格式化文字,對文字進行較複雜格式處理

正規表示式

  • 正規表示式是由正規表示式引擎實現的,最流行的是以下兩種

    • POSIX基礎正規表示式 BRE 引擎,大多數 Linux 工具至少符合 POSIX BRE 引擎規範
    • POSIX擴充套件正規表示式 ERE 引擎,提供了高階模式符號和特殊符號
  • 特殊字元

    • BRE 基礎 basic

      • \ 跳脫字元

      • 錨點字元

        • ^ 行首
        • $ 行尾
      • . 可以匹配除換行符之外的任意單個字元

      • [] 字元組,如果字元組中的某個字元出現在了資料流中,那就能匹配該模式

      • [^] 排除型字元組,匹配字元組中沒有的字元

        • 區間,比如 0-9 a-z A-Z 等範圍化
      • 特殊的字元組 [[:BRE:]] 其中的 BRE 允許下列詞

        • alnum: 任意字母或數字字元
        • alpha: 任意字母字元
        • digit: 0~9 的數字
        • lower: 小寫字母
        • upper: 大寫字母
        • print: 可列印字元
        • punct: 標點符號
        • space: 任意空白符
        • blank: 空格或製表符
      • * 表明該字元必須在匹配模式的文字中出現 0~n 次

    • ERE 擴充 extended

      • ? 表明前面的字元可以出現 0~1 次

      • + 表明前面的字元可以出現 1~n 次

      • {} 允許為正規表示式指定具體的可重複次數

        • {n} 恰好出現 n 次
        • {n,m} 恰好出現 n~m 次
      • | 以或運算進行匹配

      • () 表示式分組,每一組會被視為一個整體

瞭解圖形化 shell 程式設計

  • 建立文字選單

    • 傳統思路

      • clear: 清除使用終端會話的終端設定資訊
      • echo 命令使用 -e 選項,可以列印非可列印字元
      • read 獲取使用者輸入
    • select: 能夠幫助我們自動完成這些工作

      select variable in list
      do
          commands
      done
      
      • list: 是由空格分隔的選單項列表,該列表構成了整個選單
      • 命令會將每個列表項顯示成一個帶編號的選單項
      • PS3 環境變數 定義的特殊提示符,指示使用者做出選擇
      • 字串才是要在 case 語句中進行比較的內容,而不是跟選單選項相關聯的數字
  • 建立文字視窗部件

    • dialog 軟體包: 能夠用 ANSI 轉義控制字元,在文字環境中建立標準的視窗對話方塊

      • 使用命令列選項來決定生成哪種視窗部件

      • 要在命令列中指定某個特定部件,需要使用雙連字元格式

      • 每個dialog部件都提供了兩種輸出形式

        • 使用 STDERR,部件返回了資料會將資料傳送給 STDERR
        • 使用退出狀態碼
        • $? 變數可以確定使用者選擇了 dialog 部件中的哪個按鈕
  • 圖形化視窗部件

    • kdialog 軟體包為 KDE 桌面提供了圖形化視窗部件
    • zenity 軟體包為 GNOME 桌面提供了圖形化視窗部件