Linux 基礎學習訓練教材 - RockyLinux 9.x

第 8 堂課:bash 指令連續下達與資料流重導向

文字界面下指令的連續下達方式、資料流重新導向以及大量的應用題型!

最近更新時間: 2023/04/30

前一節課針對 bash 做簡單的變數與環境操作之介紹,本節將針對 bash 環境中常用的連續指令下達方式, 以及資料處理常用的資料流重導向與管線命令進行介紹。這些資料處理的技術對於管理員來說是相當重要的, 尤其在自撰腳本程式分析登錄檔時,這就顯的非常的重要。

8.1:連續指令的下達

某些情況下,使用者可能會連續的進行某些指令的下達。但這些指令之間可能會有關連性,例如前一個指令成功後才可進行下一個指令等。 這些情況就需要使用到特殊的字符來處理。

8.1.1:指令回傳值

前面的章節我們一直使用到 bash 終端機環境下的指令操作,而且一直接觸到很多的單引號、雙引號、錢字號、反單引號、大括號、小括號等等, 其實在該成對的符號內,隱藏了:指令、變數、計算式等的功能!再次了解特殊的符號的用途,請完成底下的練習:

例題 8.1.1-1: 請將『變數』、『指令』、『數學計算式』、『純文字』、『保持 $ 功能』等寫入下方的空格中
  1. var=${     }
  2. var=$(     )
  3. var=$((     ))
  4. var="     "
  5. var='     '
  6. var=`     `

接下來,直接應用一下上面的功能:

例題 8.1.1-2: 請找出 ifconfig 與 chfn 等指令後,列出該指令的權限
  1. 先以 which command 的方式找出指令的全名
  2. 將上述的結果,使用 ls -l 顯示出來。請搭配前一個例題的符號 ($, ', ", ` 等) 來處理。

指令的執行正確與否與後續的處理有關。Linux 環境下預設的指令正常結束回傳值為 0 ,呼叫的方式為使用『 echo $? 』即可, 亦即找出 ? 這個變數的內容即可。

例題 8.1.1-3:了解各指令回傳值的意義
  1. 輸入『 /etc/passwd 』這個檔名在指令列,之後再輸入『 echo $? 』觀察輸出的號碼為何?
  2. 輸入『 vbirdcommand 』這個指令名,之後再輸入『 echo $? 』觀察輸出的號碼為何?
  3. 因為尚未進入其他指令 (上述兩個檔名、指令都是不正確的),因此這些錯誤訊息應該來自於 bash 本身的判斷。 因此使用『 man bash 』後,查詢『 ^exit status 』關鍵字,找出上述號碼的意義為何?
  4. 承上,該段文字敘述中,解釋的回傳值共有幾個號碼?
  5. 輸入『 ls /vbird 』,之後觀察輸出的回傳值,查詢該回傳值的意義為何?(你應該要 man ls 還是 man bash 呢?)

上述的練習在讓使用者了解到,指令回傳值是每個指令自己指定的,只要符合 bash 的基本規範即可。

8.1.2:連續指令的下達

指令是可以連續輸入的,直接透過分號 (;) 隔開每個指令即可。在沒有相依性的指令環境中,可以直接進行如下的行為:

  • 列出目前的日期,直接使用 date 即可;
  • 使用 uptime 列出目前的系統資訊;
  • 列出核心資訊;
[student@localhost ~]$ date; uptime; uname -r
Sun Apr 30 03:17:19 PM CST 2023
 15:17:19 up 7 min,  2 users,  load average: 0.00, 0.04, 0.03
5.14.0-162.12.1.el9_1.0.2.x86_64

如此一口氣就可以直接將所有的指令執行完畢,無須考量其他問題。當使用者有多個指令需要下達,每個指令又需要比較長的等待時間時, 可以使用這種方式來處理即可。但是,如果想要將這些資訊同步輸出到同一個檔案時,應該如何處理?參考底下兩個範例後,說明其差異為何?

例題 8.1.2-1: 使用大括號將資料彙整的功能

請使用底下的兩個指令處理資料流,探討其原因

[student@localhost ~]$ date; uptime; uname -r > myfile.txt
[student@localhost ~]$ (date; uptime; uname -r ) > myfile.txt
  • 具有指令相依性的 && 與 ||

分號 (;) 是直接連續下達指令,指令間不必有一定程度的相依性。但當指令之間有相依性時,就能夠使用 && 或 || 來處理。 這兩個處理的方式如下:

  • command1 && command2
    當 command1 執行回傳為 0 時(成功),command2 才會執行,否則就不執行。
  • command1 || command2
    當 command1 執行回傳為非 0 時(失敗),command2 才會執行,否則就不執行。
實際練習: 當不存在 /dev/shm/check 時,就建立該目錄,若已經建立該目錄,就不做任何動作。
解析:這題雖然可以使用『 mkdir -p /dev/shm/check 』來動作,不過,如果擔心檔名指定錯誤而造成某些困擾, 那還是依據測試的結果來進行目錄的建置比較好,盡量減少使用『 mkdir -p 』的運用。我們假設該目錄的檢查為使用 ls 檢測即可。 當 ls /dev/shm/check 顯示錯誤時,表示該檔名不存在,此時才使用 mkdir (不要加上 -p 的選項) 來處理。
[student@localhost ~]$ ls -ld /dev/shm/check
ls: 無法存取 '/dev/shm/check': 沒有此一檔案或目錄
# 確認該檔名是不存在的!

[student@localhost ~]$ ls -ld /dev/shm/check || mkdir /dev/shm/check
ls: 無法存取 '/dev/shm/check': 沒有此一檔案或目錄
[student@localhost ~]$ ls -ld /dev/shm/check
drwxr-xr-x. 2 student student 40  4月 30 15:21 /dev/shm/check
# 檔案出現了!這是因為 mkdir 的緣故!

[student@localhost ~]$ ls -ld /dev/shm/check || mkdir /dev/shm/check
drwxr-xr-x. 2 student student 40  4月 30 15:21 /dev/shm/check
# 再次運作也不會出現 mkdir 的錯誤! || 會略過正確訊息的動作!
第一次執行時,由於尚未有該目錄,所以顯示找不到,但是第二次執行時,就會有該目錄,因此 mkdir 的動作就沒有進行。 若將中間分隔改為分號 (;) 時,就會產生重複 mkdir 的問題了。因此,比較好的執行方式,還是需要使用 || 較佳!
實際練習:當 /dev/shm/check 存在時,就將該目錄刪除,否則就不進行任何動作
解析:跟前面一題差不多,但是這裡是『有該目錄時,就給予刪除』,所以反而是『前面指令成功時,後面指令才運作』的概念。
[student@localhost ~]$ ls -ld /dev/shm/check && rmdir /dev/shm/check
drwxr-xr-x. 2 student student 40  4月 30 15:21 /dev/shm/check
# 兩個指令都分別順利運作完畢

[student@localhost ~]$ ls -ld /dev/shm/check && rmdir /dev/shm/check
ls: 無法存取 '/dev/shm/check': 沒有此一檔案或目錄
# 第一個 ls 的指令失敗,因此並沒有執行 rmdir 喔!若不信,使用底下指令看看:

[student@localhost ~]$ ls -ld /dev/shm/check ; rmdir /dev/shm/check
ls: 無法存取 '/dev/shm/check': 沒有此一檔案或目錄
rmdir: 刪除 '/dev/shm/check' 失敗: 沒有此一檔案或目錄
與前一個例題相似,透過 ls 簡易的進行搜尋的任務,讀者也同樣會發現兩次執行的結果並不相同。 此外,最後一個指令也能夠讓讀者知道, && 與 ; 的差異!

假設我們需要一個指令來說明某個檔名是否存在,可以這樣處理:

[student@localhost ~]$ ls -d /etc && echo exist || echo non-exist
/etc
exist
[student@localhost ~]$ ls -d /vbird && echo exist || echo non-exist
ls: 無法存取 /vbird: 沒有此一檔案或目錄
non-exist

由於我們只是想要知道該檔案是否存在,因此不需要如上表所示,連 ls 的結果也輸出。此時可以使用 &> 的方式來將結果輸出到垃圾桶,如下所示:

[student@localhost ~]$ ls -d /etc &> /dev/null && echo exist || echo non-exist
exist
[student@localhost ~]$ ls -d /vbird &> /dev/null && echo exist || echo non-exist
non-exist
例題 8.1.2-2:了解連續指令的邏輯問題
上述的指令能否寫成:『 ls -d /vbird &> /dev/null || echo non-exist && echo exist 』,嘗試說明原因。
  • 嘗試自行撰寫小程式

由於上述指令要修改很麻煩,假設我們需要使用『 checkfile filename 』來處理,此時可以撰寫一隻小腳本來進行此任務。 若該指令可以讓所有用戶執行,則可將指令寫入 /usr/local/bin 目錄內。

[root@localhost ~]# vim /usr/local/bin/checkfile
#!/bin/bash
ls -d ${1} &> /dev/null && echo "${1} exist" || echo "${1} non-exist"

[root@localhost ~]# chmod a+x /usr/local/bin/checkfile
[root@localhost ~]# checkfile /etc
/etc exist
[root@localhost ~]# checkfile /vbird
/vbird non-exist

在 checkfile 檔案中,第一行 (#!/bin/bash) 代表使用 bash 來執行底下的語法,第二行當中的變數 ${1} 代表在本檔案後面所接的第一個參數,因此執行時,就能夠直接將要判斷的檔名接在 checkfile 後面即可。

8.1.3:使用 test 及 [ 判斷式 ] 確認回傳值

事實上,前一小節使用 ls 進行確認檔名時,僅需要確認回傳值是否為 0 而已。Linux 提供一個名為 test 的指令可以確認許多檔名參數, 常見的參數有:

測試的標誌代表意義
1. 關於某個檔名的『檔案類型』判斷,如 test -e filename 表示存在否
-e該『檔名』是否存在?(常用)
-f該『檔名』是否存在且為檔案(file)?(常用)
-d該『檔名』是否存在且為目錄(directory)?(常用)
-b該『檔名』是否存在且為一個 block device 裝置?
-c該『檔名』是否存在且為一個 character device 裝置?
-S該『檔名』是否存在且為一個 Socket 檔案?
-p該『檔名』是否存在且為一個 FIFO (pipe) 檔案?
-L該『檔名』是否存在且為一個連結檔?
2. 關於檔案的權限偵測,如 test -r filename 表示可讀否 (但 root 權限常有例外)
-r偵測該檔名是否存在且具有『可讀』的權限?
-w偵測該檔名是否存在且具有『可寫』的權限?
-x偵測該檔名是否存在且具有『可執行』的權限?
-u偵測該檔名是否存在且具有『SUID』的屬性?
-g偵測該檔名是否存在且具有『SGID』的屬性?
-k偵測該檔名是否存在且具有『Sticky bit』的屬性?
-s偵測該檔名是否存在且為『非空白檔案』?
3. 兩個檔案之間的比較,如: test file1 -nt file2
-nt(newer than)判斷 file1 是否比 file2 新
-ot(older than)判斷 file1 是否比 file2 舊
-ef判斷 file1 與 file2 是否為同一檔案,可用在判斷 hard link 的判定上。 主要意義在判定,兩個檔案是否均指向同一個 inode 哩!
4. 關於兩個整數之間的判定,例如 test n1 -eq n2
-eq兩數值相等 (equal)
-ne兩數值不等 (not equal)
-gtn1 大於 n2 (greater than)
-ltn1 小於 n2 (less than)
-gen1 大於等於 n2 (greater than or equal)
-len1 小於等於 n2 (less than or equal)
5. 判定字串的資料
test -z string判定字串是否為 0 ?若 string 為空字串,則為 true
test -n string判定字串是否非為 0 ?若 string 為空字串,則為 false。
註: -n 亦可省略
test str1 == str2判定 str1 是否等於 str2 ,若相等,則回傳 true
test str1 != str2判定 str1 是否不等於 str2 ,若相等,則回傳 false
6. 多重條件判定,例如: test -r filename -a -x filename
-a(and)兩狀況同時成立!例如 test -r file -a -x file,則 file 同時具有 r 與 x 權限時,才回傳 true。
-o(or)兩狀況任何一個成立!例如 test -r file -o -x file,則 file 具有 r 或 x 權限時,就可回傳 true。
!反相狀態,如 test ! -x file ,當 file 不具有 x 時,回傳 true

test 僅會回傳 $? 而已,螢幕上不會出現任何的變化,因此如果需要取得回應,就需要使用 echo $? 的方式來查詢, 或者使用 && 及 || 來處理。

例題 8.1.3-1: 學習使用 test 指令判斷
  1. 使用 test 判斷 /etc/ 是否存在,然後顯示回傳值
  2. 使用 test 判斷 /usr/bin/passwd 是否具有 SUID ,然後顯示回傳值
  3. 使用 test 判斷 ${HOSTNAME} 是否等於 "mylocalhost" ,然後顯示回傳值
  4. 修改 /usr/local/bin/checkfile ,取消 ls 的判斷,改用 test 判斷
  • 使用中括號 [] 取代 test 進行判別式的處理

由於 test 是直接加在變數判斷之前,讀者可能偶而會覺得怪異。此時可以使用中括號 [ ] 來取代 test 的語法。 同樣以 checkfile 來處理時,該檔案的內容應該需要改寫成如下:

[root@localhost ~]# vim /usr/local/bin/checkfile
#!/bin/bash
[ -e "${1}" ] && echo "${1} exist" || echo "${1} non-exist"

由於中括號的意義非常多,包括第三堂課萬用字元當中,中括號代表的是『具有一個指定的任意字元』,未來第九堂課的正規表示法當中, 中括號也具有特殊的字符意義。而分辨是否為『判別式』的部份,就是其語法的差別。請注意,在 bash 環境下,使用中括號替代 test 指令時, 中括號的內部需要留白一個以上的空白字元!如下圖示:

[  "$HOME"  ==  "$MAIL"  ]
[□"$HOME"□==□"$MAIL"□]
 ↑       ↑  ↑       ↑
例題 8.1.3-2:使用 [] 取代 test 的用法
  1. 使用 [ ] 判斷 /etc/ 是否存在,然後顯示回傳值
  2. 使用 [ ] 判斷 /usr/bin/passwd 是否具有 SUID ,然後顯示回傳值
  3. 使用 [ ] 判斷 ${HOSTNAME} 是否等於 "mylocalhost" ,然後顯示回傳值
  • 自訂回傳值意義

如果使用者想要使用簡易的 shell script 創立一個指令,也能夠自己設定回傳值的意義。

[root@localhost ~]# vim /usr/local/bin/myls.sh
#!/bin/bash
ls ${@} && exit 100 || exit 10

[root@localhost ~]# chmod a+x /usr/local/bin/myls.sh
[student@localhost ~]$ myls.sh /vbird; echo $?
ls: 無法存取 '/vbird': 沒有此一檔案或目錄
10

上述的 ${@} 代表指令後面接的任何參數,因此你可以執行 myls.sh 後面接多個參數都沒問題。 由於 exit 可以回傳訊息,因此可以讓使用者簡易的設定好所需要的回傳訊息規範。

8.1.4:命令別名

第一堂課開始讀者應該就接到到 ls 與 ll 這兩個指令,剛開始介紹時,讀者們應該知道 ll 是 long list 的縮寫。 若將 ll 這個指令用來取代 checkfile 這個腳本,是否可以處理?

[root@localhost ~]# vim /usr/local/bin/checkfile
#!/bin/bash
#[ -e "${1}" ] && echo "${1} exist" || echo "${1} non-exist"
ll -d ${1} && echo "${1} exist" || echo "${1} non-exist"
# 特別注意,不要加上 &> /dev/null 喔!不然會看不到 ll -d 的錯誤!

[root@localhost ~]# checkfile /etc
/usr/local/bin/checkfile: 列 5: ll:命令找不到
/etc non-exist

但是,當你執行 checkfile /etc 時,竟然出現 command not found 的問題!這是為什麼?因為系統上真的沒有 ll 這個指令, 該指令為使用命令別名暫時創造出來的一個命令的別稱 (別名) 而已。要查看有多少命令別名,可以使用 alias 來查看:

[root@localhost ~]# alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'     <==指令別名!
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
......

因此讀者應該能知道為何 /bin/ls -d /etc 與 ls -d /etc 輸出的結果會有顏色差異的問題了。此外,管理員執行 mv, cp, rm 等管理檔案的指令時, 為了避免不小心導致的檔案覆蓋等問題,於是僅有 root 的身份會加上 -i 的選項,提示管理員相關的檔案覆蓋問題。 我們的 student 因為在 .bashrc 裡面也額外加入 alias 了!這才會有相關的命令別名!

例題 8.1.4-1:了解命令別名以及略過命令別名的方案
  1. 使用 root 的身份切換路徑到 /dev/shm 底下,並將 /etc 整個目錄複製到 『本目錄』 底下
  2. 若將上述指令重新執行一次,會發生什麼問題?
  3. 若你確定檔名就是需要覆蓋,那可以使用什麼方式來處理 (思考 A. 使用絕對路徑不要用命令別名 B. 讓指令自動忽略命令別名)

讓指令不以命令別名的方式執行是相當值得注意的喔!很有趣!

例題 8.1.4-2: 讓一般帳號同樣具有加上檔案覆蓋時的詢問功能
  1. 由於我們的 student 帳號也算管理員常用的帳號,因此建議也將 cp, mv, rm 預設加上 -i 的選項。請問如何處理?

8.1.5:用 () 進行資料彙整

某些時刻管理員可能需要進行一串指令後,再將這串指令進行資料的處理,而非每個指令獨自運作。如下列指令的說明:

[student@localhost ~]$ date; cal -3; echo "The following is log"
[student@localhost ~]$ date; cal -3; echo "The following is log" > mylog.txt
[student@localhost ~]$ cat mylog.txt

讀者會發現到,原本想要紀錄的資訊當中,僅有最後一個指令才可以被處理了。若需要每個指令都進行紀錄,依據前面的介紹,則必須要如此處理:

[student@localhost ~]$ date > mylog.txt; cal -3 >> mylog.txt; echo "The following is log" >> mylog.txt

指令會變得相當複雜。此時,可以透過資料統整的方式,亦即將所有的指令包含在小括號內,就能夠將訊息統一輸出了。

[student@localhost ~]$ (date; cal -3; echo "The following is log") > mylog.txt
例題 8.1.5-1:理解使用 () 處理資料的彙整
  1. 設定一個命令別名為 geterr ,內容為執行 echo "I am error" 1>&2
  2. 執行 geterr 得到什麼結果?
  3. 執行 geterr 2> /dev/null 得到什麼結果?
  4. 執行 (geterr) 2> /dev/null 得到什麼結果?
  5. 嘗試解析為何會這樣?

8.2:資料流重導向

某些時刻使用者可能需要將螢幕的資訊轉存成為檔案以方便紀錄,就是前幾堂課就已經談到過得 > 這個符號的功能。 事實上,這個功能就是資料流重導向。

8.2.1:指令執行資料的流動

從前一小節的說明,讀者可以知道指令執行後,至少可以輸出正確與錯誤的資料 ($? 是否為 0), 某些指令執行時,則會從檔案取得資料來處理,例如 cat, more, less 等指令。因此,將指令對於資料的載入與輸出彙整如下圖:

圖 8.2.1-1、指令執行過程的資料傳輸情況
圖 8.2.1-1、指令執行過程的資料傳輸情況
  • standard output 與 standard error output

簡單的說,標準輸出指的是『指令執行所回傳的正確的訊息』,而標準錯誤輸出可理解為『 指令執行失敗後,所回傳的錯誤訊息』。 不管正確或錯誤的資料都是預設輸出到螢幕上,我們可以透過特殊的字符來進行資料的重新導向!

  1. 標準輸入  (stdin) :代碼為 0 ,使用 < 或 << ;
  2. 標準輸出  (stdout):代碼為 1 ,使用 > 或 >> ;
  3. 標準錯誤輸出(stderr):代碼為 2 ,使用 2> 或 2>> ;
例題 8.2.1-1: 理解資料流的功能
  1. 以 student 身份執行找檔名的任務,指令為『 find /etc -name '*passwd*' 』,螢幕輸出什麼資訊?請了解 * 的意義。
  2. student 對於系統設定檔原本就有很多的無權限目錄,因此請將錯誤訊息直接丟棄 (導向於 /dev/null )
  3. 將最終螢幕的輸出轉存成為 ~student/find_passwd.txt 檔案
  4. 再以 student 的身份重新搜尋 /etc 底下檔名為 *shadow* 的資料,將正確訊息『累加』到 ~student/find_passwd.txt 檔案內
  5. 查看 ~student/find_passwd.txt 檔案內是否同時具有 passwd 與 shadow 相關的檔名存在?

上述的例題練習完畢後,可以將特殊的字符歸類成:

  • 1> :以覆蓋的方法將『正確的資料』輸出到指定的檔案或裝置上;
  • 1>>:以累加的方法將『正確的資料』輸出到指定的檔案或裝置上;
  • 2> :以覆蓋的方法將『錯誤的資料』輸出到指定的檔案或裝置上;
  • 2>>:以累加的方法將『錯誤的資料』輸出到指定的檔案或裝置上;

一般來說,檔案無法讓兩個程序同時打開還同時進行讀寫!因為這樣資料內容會反覆的被改寫掉,所以你不應該使用如下的方式來下達指令:

command > file.txt 2> file.txt

如果你需要將正確資料與錯誤資料同步寫入同一個檔案內,那就應該要這樣思考:

  • 將錯誤資料轉到正確資料的管線上,然後同步輸出
  • 將正確資料轉到錯誤資料的管線上,然後同步輸出
  • 所有的資料通通依序同步輸出 (不分正確與錯誤了)

若同樣使用『 find /etc -name '*passwd*' 』這個指令來處理,則讀者可以嘗試使用底下的方案來執行上述三個資料轉管道的動作:

[student@localhost ~]$ find /etc -name '*passwd*' >   ~/find_passwd2.txt 2>&1
[student@localhost ~]$ find /etc -name '*passwd*' 2>  ~/find_passwd2.txt 1>&2
[student@localhost ~]$ find /etc -name '*passwd*' &>  ~/find_passwd2.txt

但請注意指令的輸入順序, 2>&1 與 1>&2 必須要在指令的後面輸入才行。

  • standard input

有些指令在執行時,你需要敲擊鍵盤才行,這個 standard input 就可以由檔案內容來取代鍵盤輸入的意思。 舉例來說, cat 這個指令就是直接讓你敲擊鍵盤來由螢幕輸出資訊。

例題 8.2.1-2: 測試標準輸入
  1. 直接用 student 身份執行『 cat 』這個指令,然後隨意輸入兩串文字,查閱指令執行的結果為何?
  2. 要結束指令的輸入請執行 [ctrl]+d 結束 (並不是 [ctrl]+c 喔!)
  3. 若輸入的字串可以直接轉存成為 mycat.txt 檔案,該如何下達指令?
  4. 將 /etc/hosts 透過 cat 讀入 (使用兩種方式,直接讀入與透過 < 方式讀入)
  5. 將 /etc/hosts 透過 cat 以 < 的方式讀入後,累加輸出到 mycat.txt 檔案內

上面的例題中,我們可以透過 [ctrl]+d 的方式來結束輸入,但是一般用戶可能會看不懂 [ctrl]+d 代表的意義是什麼。 如果能夠使用 end 或 eof 等特殊關鍵字來結束輸入,那似乎更為人性化一些。你可以使用底下的指令來處理:

[student@localhost ~]$ cat > yourtype.txt << eof
> here is GoGo!
> eof

最後一行一定要完整的輸入 eof (上面的範例),這樣就能夠結束 cat 的輸入,這就是 << 的意義~

8.2.2:管線 (pipe) | 的意義

讀者在前幾堂課曾經看過類似『 ll /etc | more 』的指令,較特別的是管線 (pipe, |) 的功能。管線的意思是, 將前一個指令的標準輸出作為下一個指令的標準輸入來處理!流程有點像底下這樣的圖示:

圖 8.2.2-1、管線命令處理示意圖
圖 8.2.2-1、管線命令處理示意圖

另外,這個管線命令『 | 』僅能處理經由前面一個指令傳來的正確資訊,也就是 standard output 的資訊,對於 stdandard error 並沒有直接處理的能力。如果你需要處理 standard error output ,就得要搭配 2>&1 這種方式來處理才行了。

  • 管線僅會處理 standard output,對於 standard error output 會予以忽略
  • 管線命令必須要能夠接受來自前一個指令的資料成為 standard input 繼續處理才行。

常見的管線命令有:

  • cut :裁切資料,包括透過固定符號或者是固定字元位置
  • grep :擷取特殊關鍵字的功能
  • awk '{print $N}' :以空白為間隔,印出第 N 個欄位的項目
  • sort :進行資料排序
  • wc :計算資料的行數、字數、字元數
  • uniq :資料依行為單位,進行重複資料的計算
  • tee :將資料轉存一份到檔案中
  • split :將資料依據行數或容量數切割成數份
例題 8.2.2-1:我想要查看 /etc 底下共有多少檔案檔名結尾為 .conf 的檔案個數,該如何處理?
  1. 找出檔名最好使用 find 來處理,因此請使用 find /etc 來查看一下資料
  2. 你可以透過『 find /etc -name '*.conf' 』或『 find /etc | grep '\.conf' 』來找到所需要的檔名。由於 grep 可以加上顏色顯示,故建議可以嘗試使用 grep 的方式來顯示較佳。
  3. 最終計算檔案數,就可以使用 wc 這個指令來處理『 find /etc | grep '\.conf' | wc -l 』

建議使用者可以 man wc 看一下該指令的選項功能喔!

例題 8.2.2-2: 使用管線命令
  1. 在 /etc/passwd 裡面的第一欄位為帳號名稱,第七欄位為 shell ,我想要只找這兩個項目來輸出,該如何處理?
  2. 承上,若我只想要知道有多少個 shell (第七欄位),同時每個 shell 各有幾個,又可以如何處理?
  3. 使用 last 可以查看到每個用戶的登入情況,請透過 cut 取出第一個欄位後,分析每個帳號的登入次數為何
  4. 使用 ip addr show 可以查詢到每個網路界面的 IP 位址,若只想要取出 IPv4 的位址,應該如何處理?
  5. 承上,若只想要列出 IP 位址 (127.0.0.1/8 之類的),是否可以透過 awk 來達成?
  6. 承上,將取得的 IP 位址除了顯示在螢幕上,亦同步輸出到 /dev/shm/myip.txt 檔案中。

8.3:課後練習操作

  • 上課的課後練習,非作業:
  1. 簡單的操作底下的題目練習:
    1. 21世紀從 2000 到 2099 年,這 100 年當中,每四年有一次閏年,因此有 1/4 亦即 25 年有閏年 (366 天)。 請用 bash 的整數運算功能,直接算出這 100 年共有幾天?
    2. 執行這串指令:『 bash -c "ls /check &> /dev/null && exit 10 || exit 20" 』, 回傳值應該是多少?
    3. 設計一個指令,指令名稱為 mykver,當執行後,螢幕會輸出類似『 My hostname is 'localhost' and kernel version is '5.xxx' 』 這樣的字樣。
  2. 紀錄執行檔的指紋資料,請使用 md5sum 將 /usr/bin, /usr/sbin 裡面的檔案,列出檔名並排序後,以 md5sum 分析其指紋碼。 分析完畢後,將資料放至於 /root/rawdata.md5 中。
  3. 進行檔案的分割:
    1. 前往 /dev/shm/check 目錄,在該目錄下,以 dd 這個指令,建立一個 200M 的巨型檔案,檔名為 bigfile。
    2. 將該檔案以 20M 為單位,進行切割,檔名為 sfile?? 這樣,那個 ?? 為自行建立的分割檔名。
    3. 透過 cat 的功能,將 sfile?? 再次組合成為巨型檔案,檔名為 bigfile2
    4. 透過 cmp 的功能,比較 bigfile 與 bigfile2 是否為相同的檔案?
  • 作業 (不提供學生答案,僅提供教師參考答案)

作業硬碟一般操作說明:

  • 開啟雲端虛擬機器前,請務必確認你開啟的硬碟是『unit08』,否則就會做錯題目
  • 若要使用圖形界面,請務必使用 student 身份登入,若需要切換身份,再啟用終端機處理。
  • 若有簡答題需要使用中文,請自行以第一堂課的動作自行處理輸入法安裝。
  • 每部虛擬機器均有獨特的網卡位址,請勿使用他人硬碟上傳,否則計分為 0 分。
  • 每位同學均有自己的 IP 尾數,請先向老師詢問您的 IP 尾數,才可以進行作業上傳。
  • 最終上傳作業結果,請務必使用 root 身份上傳。
  • 進入作業硬碟後,先用 root 身份執行 vbird_book_setup_ip , 執行流程請參考:vbird_book_setup_ip

作業當中,某些部份可能為簡答題~若為簡答題時,請將答案寫入 /home/student/ans.txt 當中,並寫好正確題號,方便老師訂正答案。 請注意,檔名寫錯將無法上傳!

請使用 root 的身份進行如下實做的任務。直接在系統上面操作,操作成功即可,上傳結果的程式會主動找到你的實做結果。

  1. (30%)請登入這次的作業系統環境,並且依據線上的環境實做底下的題目之後,將題目的要求 (指令或者是結果) 寫入到 /root/ans08.txt 檔案內:
    1. 當執行完成 mysha.sh 這個指令之後,該指令的回傳值為多少,請寫下 (1)用什麼指令顯示回傳值? (2)回傳值是多少?
    2. 在不透過 bc 這個指令的情況下,如何以 bash 的功能,計算一年有幾秒?亦即如何計算出 60*60*24*365 的結果?(1)寫下指令, (2)寫出結果
    3. 使用 find / 找出全系統的檔名,然後將所有資料 (包括正確與錯誤) 全部寫入 /root/find_filename.txt , 請寫下達成此目標的完整指令串。
    4. 寫下一段指令 (主要以 echo 來達成的),執行該段指令會輸出『 My $HOSTNAME is 'XXX' 』,其中 XXX 為使用 hostname 這個指令所輸出的主機名稱。例如主機名稱為 station1 時,該串指令會輸出『 My $HOSTNAME is 'station1'』。 該串指令在任何主機均可執行,但都會輸出不同的訊息(因為主機名稱不一樣所致)
    5. 管理員 (root) 執行 mv 時,由於預設 alias 的關係,都會主動的加上 mv -i 這個選項。假設有個來源檔案名稱 orifile 以及目標目錄名稱 desdir , 若想要將 orifile 移動到 desdir 去的時候,請寫下兩個方法,讓 root 執行 mv 時,不會有 -i 的預設選項 (不能 unalias 的情況下)
    6. 透過『 ll /usr/sbin/* /usr/bin/* 』搭配 cut 與 sort, uniq 等指令來設計一個指令串,執行該指令串之後會輸出如下的畫面, 請寫下該指令串:(底下畫面為示意圖,實際輸出的個數可能會有些許差異)
            1 r-s--x---
            1 rw-r--r--
           10 rwsr-xr-x
            3 rws--x--x
            3 rwx------
            4 rwxr-sr-x
          241 rwxrwxrwx
            8 rwxr-x---
         1633 rwxr-xr-x
      ...
      
  2. (10%)製作一個名為 mycmdperm.sh 的腳本指令,放置於 /usr/local/bin 裡面。該腳本的重點是這樣的:
    1. 執行腳本的方式為『 mycmdperm.sh command 』,其中 command 為你想要取得的指令的名稱
    2. 在 mycmdperm.sh 裡面,指定一個變數為 cmd ,這個變數的內容為 ${1},其中 ${1} 就是該腳本後面攜帶的第一個參數
    3. 使用『 ll $( which ${cmd} ) 』來取得這個 cmd 的實際權限。
    4. 讓 mycmdperm.sh 具有可執行權。
    5. 最終請執行一次該指令,例如使用『 mycmdperm.sh passwd 』應該會秀出 passwd 的相關權限。不過該指令應該會執行失敗, 因為上述的 (c) 指令怪怪的,似乎是『命令別名無法用在腳本內』的樣子。因此,請將這個腳本的內容修訂成為沒有問題的形式。 (就是將命令別名改成實際的指令操作)
  3. (15%)製作一個名為 myfileperm.sh 的腳本指令,放置於 /usr/local/bin 裡面。該腳本的重點是這樣的:
    1. 執行腳本的方式為『 myfileperm.sh filename 』,其中 filename 為你想要取得的檔案名稱 (絕對路徑或相對路徑)
    2. 在 myfileperm.sh 裡面,指定一個變數為 filename ,這個變數的內容為 ${1},其中 ${1} 就是該腳本後面攜帶的第一個參數
    3. 判斷 filename 是否不存在,若不存在則回報『filename is non exist』
    4. 判斷 filename 存在,且為一般檔案,若是則回報『 filename is a reguler file 』
    5. 判斷 filename 存在,且為一般目錄,若是則回應『 filename is a directory』
  4. (15%)製作一個 mymsg.sh 的腳本指令,放置於 /usr/local/bin 底下:
    1. 當執行 mymsg.sh 時,螢幕會輸出底下的字樣,然後結束指令。
      [student@localhost ~]$ mymsg.sh
      Hollo!!
      My name is 'Internet Lover'...
      My server's kernel version is $kver
      I'm a student
      bye bye!!
      
    2. 上面的輸出不能使用 echo,請使用 cat 搭配 << eof 這樣的指令語法來處理資料輸出的資訊。
  5. (10%)檔案與檔案內容處理方法:
    1. 找出 /etc/services 這個檔案內含有 http 的關鍵字那幾行,且 http 只會出現在行首!並將該資料轉存成 /root/myhttpd.txt 檔案
    2. 找出 examuser 這個帳號在系統所擁有的檔案,並將這些檔案『移動』到 /root/examuser 目錄中
  6. (10%)建立一般帳號也可以執行的指令 (全部的指令請放置到 /usr/local/bin 目錄下)
    1. 做一個名為 myip 的指令,這個指令會透過 ifconfig 的功能,顯示出 ens3 這張網卡的 IP (只要 IP 就好喔!)。例如 IP 為 192.168.251.12 時,則輸入 myip 這個指令,螢幕只會輸出 192.168.251.12 的意思。
    2. 建立一個名為 myerr 的指令,這個指令會將『 echo "I am error message" 』這個訊息傳輸到 standard error output 去! 亦即當執行『 myerr 』時,會在螢幕上出現 I am error message,但是執行『 myerr 2> /dev/null 』時,螢幕不會有任何訊息的輸出。
  7. (10%)建立一個名為 /root/split 的目錄,進行如下的行為:
    1. 將 /etc/services 複製到本目錄下
    2. 假設 services 容量太大了,現在請以 100K 為單位,將該檔案拆解成 file_aa, file_ab, file_ac.. 等檔名的檔案, 每個檔案最大為 100K (請自行 man split 去處理)

作業結果傳輸:請以 root 的身分執行 vbird_book_check_unit 指令上傳作業結果。 正常執行完畢的結果應會出現【XXXXXX_aa:bb:cc:dd:ee:ff_unitNN】字樣。若需要查閱自己上傳資料的時間, 請在作業系統上面使用瀏覽器查詢: http://192.168.251.254 檢查相對應的課程檔案。 相關流程請參考: vbird_book_check_unit

修改歷史:
  • 2023/04/30:bash shell 的變化不大!修改的幅度較低!不過作業部份尚未處理!
  • 2023/05/02:alias 在非互動的 script 當中,真的很難檢測,所以將命令別名的部份修改成為指令來處理了!
2023/04/30 以來統計人數
計數器
其他連結
環境工程模式篇
鳥園討論區
鳥哥舊站

今日 人數統計
昨日 人數統計
本月 人數統計
上月 人數統計