第 7 堂課:認識 bash 基礎與系統救援
前一節課談到檔案系統且最終有一個簡單的檔案系統錯誤救援。但如果發生嚴重問題時該如何是好?此時可能需要一個簡易的救援模式, 包括透過 systemd 以及直接取得一個 bash 來處理。那如何使用 bash ?這就需要了解一下 bash shell 的功能了。
- 7.1:bash shell 基礎認識
- 7.1.1:系統與使用者的 shell
- 7.1.2:變數設定規則
- 7.1.3:影響操作行為的變數
- 7.1.4:區域/全域變數、父程序與子程序
- 7.1.5:使用 kill 管理程序
- 7.1.6:login shell and non-login shell
- 7.2:系統救援
- 7.2.1:透過正規的 systemd 方式救援
- 7.2.2:透過 bash 直接救援 (Optional)
- 7.3:課後練習操作
7.1:bash shell 基礎認識
之前課程講過登入系統取得的文字型互動界面就稱為 shell,shell 的操作環境能夠依據使用者的喜好來設定,使用者也能夠切換不同的 shell。 而 shell 最重要的就是變數,這在許多的程式語言裡面都是需要注意到的部份。
7.1.1:系統與使用者的 shell
系統所有合法的 shell 都在 /etc/shells 這個檔案內,讀者可以查詢該檔案的內容。在 /etc/shells 常見的合法 shell 如下:
- /bin/sh (已經被 /bin/bash 所取代)
- /bin/bash (就是 Linux 預設的 shell)
- /bin/tcsh (整合 C Shell ,提供更多的功能)
- /bin/csh (已經被 /bin/tcsh 所取代)
因為有許多軟體會使用到系統上的 shell,但又擔心使用者或者是惡意攻擊者會使用怪異的有問題的 shell 來操作軟體。因此某些軟體在判斷 shell 的合法性, 亦即直接參考 /etc/shells 的規範,來判斷所謂的合法與不合法。
讀者從之前的系統登入行為中,應該知道在文字界面登入後系統會給予一個 shell,而在圖形界面時,也能夠藉由按下『終端機』來取得 shell 的操作。 但取得的預設 shell 是哪一個?需要從使用者的設定資料裡面搜尋。請參考 /etc/passwd 裡面,使用冒號 (:) 分隔的第 7 個欄位,就是該帳號預設取得的 shell。
例題:
|
使用者可以自由的切換所需要的 shell,不過不同的 shell 使用的方式、語法都有點差異。舉例來說, bash 使用的變數設定方式為 『var='content'』, 但是 csh 使用的是『 set var = 'content' 』,csh 需要有 set ,不過等號兩邊可以有空格。但是 bash 雖然不用 set ,但是等號兩邊不可以直接加空格, 這就有不一樣的的地方。
例題:練習不同 shell 的切換
|
使用者可以透過直接輸入 shell 的執行檔 (例如上述的 /bin/csh) 來直接切換到新的 shell 去工作,而想要確認目前的 shell 是什麼, 最簡單的方式就是使用『 echo $0 』列出目前的執行檔。另外,寫入在 /etc/shells 內有個名為 /sbin/nologin 的 shell,就是給系統帳號預設使用的不可互動的合法 shell 。
例題:
|
為何需要設定 /sbin/nologin 呢?
- 許多系統預設要執行的軟體,例如 mail 的郵件分析、WWW 的網頁回應等等,系統不希望該軟體使用 root 的權限,因為擔心網路軟體會被惡意人士所攻擊。 因此系統就會依據該軟體的特性給予『系統帳號』,這些系統帳號就是有特殊的任務(執行某軟體)而產生的,並不是要讓一般用戶透過該帳號來登入系統互動。 因此系統帳號通常就是使用 /sbin/nologin 作為預設 shell
- 某些伺服器的帳號,例如郵件伺服器、FTP 伺服器等,這些伺服器的帳號本收就在收發 email 或者是傳輸檔案而已,這些帳號無須登入系統來取得互動 shell, 因此這些帳號就不需要可互動的 shell,此時就能給 /sbin/nologin。
例題:
|
7.1.2:變數設定規則
上一小節談到的『 echo $BASH 』就是變數的功能,bash shell 會主動的建立 BASH 這個變數,且其內容就是 /bin/bash。 方便讀者了解到目前的 shell 是哪隻程式達成的。如何設定變數呢?簡單的設定方式與呼叫方式為:
[student@localhost ~]$ 變數="變數內容" [student@localhost ~]$ echo $變數 [student@localhost ~]$ echo ${變數} |
其中變數有許多的設定規則需要遵守:
- 變數與變數內容以一個等號『=』來連結
- 等號兩邊不能直接接空白字元
- 變數名稱只能是英文字母與數字,但是開頭字元不能是數字
- 變數內容若有空白字元可使用雙引號『"』或單引號『'』將變數內容結合起來
- 承上,雙引號內的特殊字元如 $ 等,可以保有原本的特性
- 承上,單引號內的特殊字元則僅為一般字元 (純文字)
- 可用跳脫字元『 \ 』將特殊符號(如 [Enter], $, \, 空白字元, ' 等)變成一般字元
- 在一串指令的執行中,還需要藉由其他額外的指令所提供的資訊時,可以使用反單引號『`指令`』或 『$(指令)』。
- 若該變數為擴增變數內容時,則可用 "$變數名稱" 或 ${變數} 累加內容
- 若該變數需要在其他子程序執行,則需要以 export 來使變數變成環境變數
- 通常大寫字元為系統預設變數,自行設定變數可以使用小寫字元,方便判斷 (純粹依照使用者興趣與嗜好)
- 取消變數的方法為使用 unset :『unset 變數名稱』
例題:
|
變數設定的過程當中,使用子指令『 $(command) 』的操作為相當重要的。例如底下的案例中,管理員可以很快速的找到前一堂課談到的特殊權限檔案並列出該檔案的權限:
例題:
|
如上的第 3 個範例,讀者可以將第 1 個範例的檔名找到後,一個一個以 ls -l 去查詢權限,不過效能與時間花費太多。 此時透過子指令的功能即可快速的找到相對應的資料。底下亦為常見的操作功能:
例題:
|
7.1.3:影響操作行為的變數
某些變數會影響到使用者的操作行為,許多變數之前曾經提及,本節集中說明。
變數 | 功能 |
LANG LC_ALL |
語系資料,例如使用 date 輸出資訊時,透過 LANG 可以修改輸出的訊息資料。 |
PATH | 執行檔搜尋的路徑~目錄與目錄中間以冒號(:)分隔,由於執行檔/指令的搜尋是依序由 PATH 的變數內的目錄來查詢,所以,目錄的順序也是重要的。 |
HOME | 代表使用者的家目錄,亦即使用者看到的 ~ 代表的目錄。 |
當我們使用 mail 這個指令在收信時,系統會去讀取的郵件信箱檔案 (mailbox)。 | |
HISTSIZE | 這個與『歷史命令』有關。我們曾經下達過的指令可以被系統記錄下來,而記錄的『筆數』則是由這個值來設定的。 |
RANDOM | 『隨機亂數』的變數。目前大多數的 distributions 都會有亂數產生器,亦即 /dev/random 檔案。讀者可以透過這個亂數檔案相關的變數 ($RANDOM) 來隨機取得亂數。在 BASH 的環境下,RANDOM 變數的內容介於 0~32767 之間,所以,你只要 echo $RANDOM 時,系統就會主動的隨機取出一個介於 0~32767 的數值。 |
PS1 | 命令提示字元,可使用 man bash 搜尋 PS1 關鍵字,即可了解提示字元的設定方式。 |
? | $? 這個變數內容為指令的回傳值,當回傳值為 0 代表指令正常運作結束,當不為 0 則代表指令有錯誤。 |
比較需要注意到的變數是 PATH 路徑搜尋變數,他會影響到使用者操作的行為,設定錯誤會有相當嚴重的後果。
例題:關於 PATH 的重要性
|
命令提示字元在每個系統中都不一樣,但那是可以修改的,就透過 PS1 這個變數來修改即可。
例題:
|
7.1.4:區域/全域變數、父程序與子程序
變數是有使用範圍的,一般來說變數的使用範圍分為:
- 區域變數:變數只能在目前這個 shell 當中存在,不會被子程序所沿用
- 全域變數:變數會儲存在一個共用的記憶體空間,可以讓子程序繼承使用。
如 7.1.2 當中提到的將變數提昇成為全域變數的方式為透過 export,觀察可用 env 或 export 來觀察。
例題:
|
基本上,由原本的 bash 衍生出來的程序都是該 bash 的子程序,而 bash 可以執行 bash 產生一隻 bash 的子程序,兩隻 bash 之間僅有全域變數 (環境變數) 會帶給子程序, 而子程序的變數,基本上是不會回傳給父程序的。
7.1.5:使用 kill 管理程序
某些時刻管理員會有想要手動移除某些特定程序的時刻發生,例如某些很佔資源的 bash 程序的管理等等,此時就可以透過使用 kill 這個指令來處理。 基本上,kill 並不是『刪除』程序,而是給予程序一個『訊號 (signal) 』來管理,預設的訊號為 15 號,該訊號的功能為『正常關閉程序』的意思。 而想要強制關閉該程式,就得要使用 -9 這個號碼來處理了。
例題:
|
若使用者有特別的需求需要刪除掉某些特定的程序,就可以透過這樣的機制來處理。
7.1.6:login shell and non-login shell
當讀者下達『 echo ${PS1} 』時,應該有發現 PS1 這個影響操作行為的變數已經設定好了,故應該理解為已經有設定檔在協助使用者登入時規劃好操作環境的流程。 而讀者應該也會發現到,取得 bash 的情況有很多種,但大致可分為兩大類:
- 一種是需要輸入帳號與密碼才能夠取得 bash 的行為,例如從 tty2 登入,或者是輸入『 su - 』來取得某個帳號的使用權,這種情況被稱為是 login shell 的變數設定檔讀取方式。
- 一種是使用者已經取得 bash 或者是其他的互動界面,然後透過該次登入後執行 bash ,例如從圖形界面按下終端機、直接在文字界面輸入 bash 來取得 bash 子程序、輸入『 su 』來切換身份等等, 這種方式通常不需要重新輸入帳號與密碼,因此稱為 non-login shell 的變數設定檔讀取方式。
通常 login shell 讀取設定檔的流程是:
- /etc/profile:這是系統整體的設定,你最好不要修改這個檔案;
- ~/.bash_profile 或 ~/.bash_login 或 ~/.profile (只會讀 1 個,依據優先順序決定):屬於使用者個人設定,你要改自己的資料,就寫入這裡!
由於 login shell 已經讀取了 /etc/profile 因此已經設定了大部分的全域變數設定,所以 non-login shell 只需要少部份的設定即可。 故 non-login shell 只會讀取一個個人設定檔,亦即是:
- ~/.bashrc
例題:
|
由於 ~/.bash_profile 也是讀取 ~/.bashrc ,因此使用者只需要將設定放置於家目錄下的 .bashrc 就可以讓兩者讀取了。
例題:嘗試設定 student 的操作環境
|
當使用者登出 bash 時,bash 會依據家目錄下的 .bash_logout 來進行後續的動作,因此若使用者有需要額外進行某些工作時, 可以在此檔案中設定。
不過使用者應該要特別注意的, .bash_logout 僅會在 login-shell 的環境下,登出才會執行。在 non-login shell 的環境下登出, 這個檔案並不會被運作的。
例題:
|
7.2:系統救援
我們在前一堂課提到過簡易的系統救援,直接以 root 的密碼與身份來進入救援模式,然後處理好了檔案系統。但萬一 root 的 shell 被不小心修改了, 導致無法使用 root 的密碼進入系統時,該如何處理?底下的動作比較嚴重,請讀者務必要學會救援的方式。
7.2.1:透過正規的 systemd 方式救援
要處理這個練習,請先進行如下的動作之後,讓系統被破壞,才能夠加以練習:
[root@localhost ~]# vim /etc/fstab /dev/mapper/centos-home1 /home xfs defaults 0 0 [root@localhost ~]# usermod -s /sbin/nologin root [root@localhost ~]# su - This account is currently not available. |
如此才能夠確認 1. 檔案系統被破壞以及 2. root 的身份設定錯誤 (shell 無法使用)。接下來執行 reboot 來看看會出現什麼問題。
如上圖所示,開機後出現檔案系統狀態,系統要求輸入 root 密碼,雖然用戶輸入正確的密碼了,卻無法取得 root 的 bash 操作界面, 因為設定到錯誤的 shell 了。此時正規的救援模式無法使用,需要用到 systemd 開機流程裡面,讓系統進入到一個小小的救援作業系統, 該作業系統是模擬出來的,僅僅用來作為掛載檔案系統的功能。處理的流程為:
- 重新開機,且進入選單後的五秒內按下方向鍵,並將光棒選擇到第一個開機選單項目上面
圖7.2.2、光棒的移動與進入編輯模式的參考按鈕 (e) - 按下『 e 』來進入選單編輯畫面,如下所示,並且在 linux16 那一行的最後面增加『 rd.break 』的項目,之後按下 [Ctrl]+x 來進入救援模式
圖7.2.3、互動編輯核心參數,加入進入救援模式的方案 - 救援模式會將根目錄掛載到 /sysroot 這個目錄下,不過是預設掛載成唯讀,因此管理員請用『 mount -o remount,rw /sysroot 』將該目錄重新掛載成可讀寫,
然後使用『 chroot /sysroot 』將根目錄切換到 /sysroot 底下,就可以成功的取用原本的作業系統了。
圖7.2.4、重新掛載為可讀寫,並進行更換根目錄的行為 (chroot) - 此時系統提示字元只有『 sh-4.2# 』也是正常的,請執行『 mount -a 』之後就可以進行『 usermod -s /bin/bash root 』等動作!一般建議動作流程如下:
[root@localhost ~]# mount -a [root@localhost ~]# usermod -s /bin/bash root [root@localhost ~]# vim /etc/fstab /dev/mapper/centos-home /home xfs defaults 0 0 [root@localhost ~]# touch /.autorelabel [root@localhost ~]# exit
- 最後使用 reboot 重新開機,就能夠正常的開機進入系統環境了。
最後一個步驟之所以要處理 /.autorelabel 這個情況,是因為 CentOS 7 預設會啟用 SELinux 這個安全強化模組,但是此模組在救援模式並沒有開啟, 所以被更動到的檔案在下次開機後,可能會產生無法讀取的問題。系統開機會去找 /.autorelabel ,若發現有此檔案則會重新寫入 SELinux 的相關設定, 因此系統在重新開機的流程中共會啟動兩次,原因為第一次會重新寫入 SELinux 設定,第二次才是正常開機。
7.2.2:透過 bash 直接救援 (Optional)
若讀者以前有接觸過 Linux 的話,應該知道開機流程中,可以使用 init=/bin/bash 直接讓核心呼叫 bash 來操作系統。 CentOS 7 的 grub2 與 systemd 也保留此功能,此操作行為與 rd.break 相當接近。
- 同樣在開機過程中,使用第一個開機選單,然後按下 e 來進入互動編輯模式
- 在 linux16 那一行的最後面加入 init=/bin/bash 然後按下 [ctrl]+x 來啟動
- 出現『 bash-4.2# 』之後,進行如下的動作來處理:
[root@localhost ~]# mount -o remount,rw / [root@localhost ~]# mount -a [root@localhost ~]# /usr/sbin/usermod -s /bin/bash root [root@localhost ~]# /usr/bin/vim /etc/fstab [root@localhost ~]# /usr/bin/touch /.autorelabel [root@localhost ~]# reboot
因為這個方案是 bash 直接控管的,與 systemd 的管理機制無關,因此使用者無法使用 reboot 來重新開機。此時請按下電腦的 reset 按鈕或強制關機再次重新開機。 之後應該就可以順利的開機了。
7.3:課後練習操作
前置動作:請使用 unit07 的硬碟進入作業環境,並請先以 root 身分執行 vbird_book_setup_ip 指令設定好你的學號與 IP 之後,再開始底下的作業練習。
請使用 root 的身份進行如下實做的任務。直接在系統上面操作,操作成功即可,上傳結果的程式會主動找到你的實做結果。
- 因為某些緣故,目前這個作業系統應該是無法順利開機的。請使用本節課程所介紹的方式來進行系統的救援。 根據猜測,可能的原因與管理員曾經動過 chsh 這個指令有關,同時,管理員似乎也更動過 fstab 這個設定檔。 請依據這些之前的可能舉動,來恢復系統的可登入狀態。(hint: 千萬不要忘記 .autorelabel 的動作!)
- 請處理底下帳號與 shell 的相關事宜:
- 請將系統中的 /bin/false 與 /bin/true 這兩個檔案變成合法的 shell
- 請將 examuser1 的 shell 變成 /bin/true
- 因為你即將建立一個 FTP 伺服器,這個伺服器上面的用戶只能單純的使用 FTP 功能,因此你想要讓這些帳號無法使用 shell。 假設三個帳號 myuser1, myuser2, myuser3 的帳號,這三個帳號將無法使用互動界面操作系統,且密碼為 MyPassWordhehe
- 透過 bash shell 的功能,進行檔案的查詢與複製
- 找出系統中檔案擁有者為 examuser1 的檔名,並將這些找到的檔名(含權限)複製到 /root/findout/ 目錄內
- 找出在 /usr/sbin 及 /usr/bin 底下權限為 4755 的檔案,並將這些檔案複製到 /root/findperm/ 目錄內
- 在你的系統中,嘗試找到一個執行 sleep 的程序,並且使用各種方法,將該程序刪除。
- 建立名為 examuser10 的帳號,密碼為 MyPassWordhehe,且這個帳號登入後,預設會有底下的設定:
- 登入行為:
- 預設使用 bash 作為 shell
- 會讀入 /etc/examvar 設定檔
- 擁有一個名為 myip 的變數,變數內容為『 ifconfig eth0 | grep 'inet ' | cut -d 't' -f 2 | cut -d ' ' -f 2 』 的執行成果 (每個同學操作指令的結果都不會相同,但是指令會是一樣的)
- 使用 zh_TW.utf8 語系資料
- 增加 ~examuser10/scripts/ 目錄作為指令執行時所尋找的目錄位置
- 命令提示字元增加時間項目在裡面
- 預設歷史命令紀錄 5000 筆
- 操作 cp 時,自動給予 cp -i 的選項
- 透過『 wc -l ~/.bash_history | cut -d ' ' -f 1 』的指令取得前一次登入的歷史命令次數,並將該數值轉存成為 ~/.history_start
- 登出的行為:
- 透過『 history | tail -n 1 | awk '{print $1}' 』取得最後一筆歷史紀錄,然後將該數值指定為 hist_end 變數
- 請使用『 cat ~/.history_start 』取得登入時記載的歷史命令筆數,將該數值指定為 hist_start 變數
- 設定一個名為 hist_size 的變數,內容為 hist_end - hist_start 的數值 (有多種計算方式,能成功即可!)
- 將離開的日期,使用『 YYYY/MM/DD HH:MM 』的格式累加寫入到 ~/history.log 檔案中
- 透過『 history $hist_size 』取得最後最新的數筆紀錄後,將該資料累加到 ~/history.log 檔案中。
- 登入行為:
作業結果傳輸:請以 root 的身分執行 vbird_book_check_unit 指令上傳作業結果。 正常執行完畢的結果應會出現【XXXXXX;aa:bb:cc:dd:ee:ff;unitNN】字樣。若需要查閱自己上傳資料的時間, 請在作業系統上面使用: http://192.168.251.250 檢查相對應的課程檔案。
2017/03/20:增加了習題了!
2016/07/14以來統計人數