正規表示法用在非常多地方!熟悉正規表示法好處多多!因為其他程式語言也會用到這玩意兒喔!
談完了指令列的 bash shell 的操作,再來得要思考一下,如果管理員有一堆指令要循序進行,且這些指令可能具有相依性 (例如判斷式), 或者是管理員需要寫一些讓使用者『互動』的指令碼時,應該如何處理?這時就得要透過 bash 的 shell script (程式化腳本) 來進行了。 此外,很多時候我們需要進行資料的擷取,這時好用的正規表示法就得要派上用場了!
使用者在操作電子郵件時,常常會發現到許多郵件被丟到垃圾桶或者是被判定為病毒郵件,這些判定的方式,很多就是透過『正規表示法』來處理的! 正規表示法 (Regular Expression) 就是處理字串的方法,他是以行為單位來進行字串的處理行為,正規表示法透過一些特殊符號的輔助, 可以讓使用者輕易的達到『搜尋/刪除/取代』某特定字串的處理程序!
由於正規表示法牽涉到資料的擷取,因此讀者們先了解一下最簡單的資料擷取指令: grep 的進階用法。例如,找出 /etc/passwd 當中, 含有 student 的那行,且列出行號:
[student@localhost ~]$ grep -n student /etc/passwd 37:student:x:1000:1000:student:/home/student:/bin/bash
讀者們會看到輸出的資訊中,最前面會多出一個行號的資訊,就可以讓使用者知道該資訊來自檔案的那一行這樣。 另外,當使用者有觀察開機流程所產生的資訊時,例如想要查詢開機過程產生的問題等等,可以使用 dmesg 這個指令。 只是這個指令輸出的資訊量非常龐大。若用戶僅須知道某張網路卡的相關資訊,那該如何處理呢? 先讓我們來查詢一下網路卡的代號:
# 單純使用 dmesg 的情況下,資料量相當龐大 [student@localhost ~]$ dmesg ...... [ 2.142807] libata version 3.00 loaded. [ 2.149320] ata_piix 0000:00:01.1: version 2.13 [ 2.153260] vda: vda1 vda2 vda3 [ 2.157591] scsi host0: ata_piix [ 2.166390] scsi host1: ata_piix ...... # 使用底下的方法可以找到網路卡的代號 [student@localhost ~]$ ip link show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 link/ether 52:54:00:ba:0b:fc brd ff:ff:ff:ff:ff:ff altname enp0s3
上面輸出的 ens3 就是網卡代號,而 lo 則是內部迴圈測試網卡,那個可以暫時略過不理。 現在,讓我們從 dmesg 的開機訊息中,找出網路卡相關的訊息吧:
[student@localhost ~]$ dmesg | grep -n -i ens3
548:[ 2.191080] virtio_net virtio0 ens3: renamed from eth0
大概在 548 行是比較重要的項目。那如果我們還需要知道該行之前 (before) 的 4 行以及之後 (after) 的 3 行, 以了解這行前後文的話,則可以這樣處理:
[student@localhost ~]$ dmesg | grep -n -A 3 -B 4 -i ens3 544-[ 2.157591] scsi host0: ata_piix 545-[ 2.166390] scsi host1: ata_piix 546-[ 2.166428] ata1: PATA max MWDMA2 cmd 0x1f0 ctl 0x3f6 bmdma 0xc000 irq 14 547-[ 2.166430] ata2: PATA max MWDMA2 cmd 0x170 ctl 0x376 bmdma 0xc008 irq 15 548:[ 2.191080] virtio_net virtio0 ens3: renamed from eth0 <==以這行為基準 549-[ 2.200898] cirrus 0000:00:02.0: vgaarb: deactivate vga console 550-[ 2.270108] Console: switching to colour dummy device 80x25 551-[ 2.270401] [drm] Initialized cirrus 2.0.0 2019 for 0000:00:02.0 on minor 0
正規表示法既然是透過一些字符來作為資料擷取的判斷,那麼有哪些慣用的符號呢?大概有底下這些基本的符號:
RE 字符 | 意義與範例 |
^word | 意義:待搜尋的字串(word)在行首! 範例:搜尋行首為『 # 』開始的那一行,並列出行號 grep -n '^#' regular_express.txt |
word$ | 意義:待搜尋的字串(word)在行尾! 範例:將行尾為『 ! 』的那一行列印出來,並列出行號 grep -n '!$' regular_express.txt |
. | 意義:代表『一定有一個任意字元』的字符! 範例:搜尋的字串可以是 (eve) (eae) (eee) (e e), 但不能僅有 (ee) !亦即 e 與 e 中間『一定』僅有一個字元,而空白字元也是字元! grep -n 'e.e' regular_express.txt |
\ | 意義:跳脫字符,將特殊符號的特殊意義去除! 範例:搜尋含有單引號『 ' 』的那一行! grep -n \' regular_express.txt |
* | 意義:重複零個到無窮多個的前一個 RE 字符 範例:找出含有 (es) (ess) (esss) 等等的字串,注意,因為 * 可以是 0 個,所以 es 也是符合帶搜尋字串。另外,因為 * 為重複『前一個 RE 字符』的符號, 因此,在 * 之前必須要緊接著一個 RE 字符喔!例如任意字元則為 『.*』 ! grep -n 'ess*' regular_express.txt |
[list] | 意義:字元集合的 RE 字符,裡面列出想要擷取的字元! 範例:搜尋含有 (gl) 或 (gd) 的那一行,需要特別留意的是,在 [] 當中『謹代表一個待搜尋的字元』, 例如『 a[afl]y 』代表搜尋的字串可以是 aay, afy, aly 即 [afl] 代表 a 或 f 或 l 的意思! grep -n 'g[ld]' regular_express.txt |
[n1-n2] | 意義:字元集合的 RE 字符,裡面列出想要擷取的字元範圍! 範例:搜尋含有任意數字的那一行!需特別留意,在字元集合 [] 中的減號 - 是有特殊意義的,他代表兩個字元之間的所有連續字元!但這個連續與否與 ASCII 編碼有關,因此,你的編碼需要設定正確(在 bash 當中,需要確定 LANG 與 LANGUAGE 的變數是否正確!) 例如所有大寫字元則為 [A-Z] grep -n '[A-Z]' regular_express.txt |
[^list] | 意義:字元集合的 RE 字符,裡面列出不要的字串或範圍! 範例:搜尋的字串可以是 (oog) (ood) 但不能是 (oot) ,那個 ^ 在 [] 內時,代表的意義是『反向選擇』的意思。 例如,我不要大寫字元,則為 [^A-Z]。但是,需要特別注意的是,如果以 grep -n [^A-Z] regular_express.txt 來搜尋,卻發現該檔案內的所有行都被列出,為什麼?因為這個 [^A-Z] 是『非大寫字元』的意思, 因為每一行均有非大寫字元,例如第一行的 "Open Source" 就有 p,e,n,o.... 等等的小寫字 grep -n 'oo[^t]' regular_express.txt |
\{n,m\} | 意義:連續 n 到 m 個的『前一個 RE 字符』 意義:若為 \{n\} 則是連續 n 個的前一個 RE 字符, 意義:若是 \{n,\} 則是連續 n 個以上的前一個 RE 字符! 範例:在 g 與 g 之間有 2 個到 3 個的 o 存在的字串,亦即 (goog)(gooog) grep -n 'go\{2,3\}g' regular_express.txt |
另外,由於字元擷取通常會有大小寫、數字、特殊字元等等的差異,因此我們也能夠使用如下的符號來代表某些特殊字元:
特殊符號 | 代表意義 |
[:alnum:] | 代表英文大小寫字元及數字,亦即 0-9, A-Z, a-z |
[:alpha:] | 代表任何英文大小寫字元,亦即 A-Z, a-z |
[:blank:] | 代表空白鍵與 [Tab] 按鍵兩者 |
[:cntrl:] | 代表鍵盤上面的控制按鍵,亦即包括 CR, LF, Tab, Del.. 等等 |
[:digit:] | 代表數字而已,亦即 0-9 |
[:graph:] | 除了空白字元 (空白鍵與 [Tab] 按鍵) 外的其他所有按鍵 |
[:lower:] | 代表小寫字元,亦即 a-z |
[:print:] | 代表任何可以被列印出來的字元 |
[:punct:] | 代表標點符號 (punctuation symbol),亦即:" ' ? ! ; : # $... |
[:upper:] | 代表大寫字元,亦即 A-Z |
[:space:] | 任何會產生空白的字元,包括空白鍵, [Tab], CR 等等 |
[:xdigit:] | 代表 16 進位的數字類型,因此包括: 0-9, A-F, a-f 的數字與字元 |
請操作者進行如下的例題來處理相關任務:
sed 也是支援正規表示法的一項工具軟體,具有很多很好用的功能在內!過去我們曾經使用過 ifconfig 與 awk 來找到 IP , 現在讓我們使用 sed 來處理 IP 的設定。最基礎的 sed 功能為取代,如下所示:
[student@localhost ~]$ sed 's/舊字串/新字串/g' 檔案內容
使用者只要替換『新舊字串』內容,即可處理相關的字串修訂。現在讓我們來處理 IP 的擷取。使用 ifconfig ens3 來輸出網路資料, 之後以 grep 取出 inet 那一行 (注意: ens3 請依據你的系統網卡來調整):
[student@localhost ~]$ ifconfig ens3 | grep 'inet[[:space:]]'
inet 172.16.5.237 netmask 255.255.0.0 broadcast 172.16.255.255
使用 sed 取代開頭到 inet 空白的項目:
[student@localhost ~]$ ifconfig ens3 | grep 'inet[[:space:]]' | \ > sed 's/^.*inet[[:space:]]//g' 172.16.5.237 netmask 255.255.0.0 broadcast 172.16.255.255
再接著取消空白netmask 之後的訊息
[student@localhost ~]$ ifconfig ens3 | grep 'inet[[:space:]]' | \ > sed 's/^.*inet[[:space:]]//g' | \ > sed 's/[[:space:]]*netmask.*$//g' 172.16.5.237
除了替換資料之外,sed 還可以擷取出特定的關鍵行數,例如只想要取出 10-15 行的 /etc/passwd 內容時,可以這樣做:
[student@localhost ~]$ cat -n /etc/passwd | sed -n '10,15p'
上面這個動作在處理一些腳本化程式時,相當有幫助!而如果想要直接修改檔案內容時,例如想要將 .bashrc 內的 function 改成大寫時, 也可以這樣做:
[student@localhost ~]$ grep -n 'function' ~/.bashrc 18:# User specific aliases and functions [student@localhost ~]$ sed 's/function/FUNCTION/g' ~/.bashrc | cat -n ....... 18 # User specific aliases and FUNCTIONs >==看第 18 行,這裡會變大寫 [student@localhost ~]$ sed -i 's/function/FUNCTION/g' .bashrc
加上 -i 選項後,該改變直接寫入檔案,且不會在螢幕上輸出了!因此使用上需要特別注意!
shell script 對管理員來說,是一項非常好用的工具!請讀者們一定要自己手動設計過一次相關的腳本程式, 而且能夠針對自己管理的伺服器進行一些例行工作的優化,才會更有感覺。
shell script 的撰寫其實沒有很難,基本上需要注意到:
至於 shell script 的執行,例如有個檔案名為 /home/student/shell.sh 的 script 時,可以用底下的方法:
底下我們將使用 student 的身份,並在 ~/bin 底下建立多個 shell script 來作為練習。首先,如果操作者執行 myid.sh 時, 系統會輸出這個帳號的 id 指令輸出訊息,並且輸出使用者的家目錄 (${HOME})、以及歷史命令紀錄筆數 (${HISTSIZE}), 最後列出所有的命令別名 (alias) 時,我們可以這樣做:
上述的結果一項一項撰寫成為 myid.sh 的內容如下:
[student@localhost ~]$ mkdir bin [student@localhost ~]$ cd bin [student@localhost bin]$ vim myid.sh #!/bin/bash # This script will use id, echo to show account's messages # write by VBird 2016/04/27 echo "This script will show your accout messages." echo "The 'id' command output is: " id echo "your user's home is: ${HOME}" echo "your history record: ${HISTSIZE}" echo "your command aliases: " alias echo "your home dir's filenames: " ll ~
之前曾經談過程序的觀察,且程序之間是有相依性的,因此可以使用 pstree 來觀察程序的相依行為。 那麼使用 shell script 時,他與當前的 shell 有無關係呢?底下舉例來瞧瞧。如果你有一個如下的腳本, 該如何進入到該目錄去?
[student@localhost ~]$ cd ~/bin [student@localhost bin]$ vim gototmp.sh #!/bin/bash # this shell script will take you togo /tmp directory. # VBird 2016/05/02 cd /tmp pwd [student@localhost bin]$ chmod a+x gototmp.sh
事實上,執行腳本有基本的兩種方式:
上述的第二種就是透過 source 或 . 來處理的。現在,請使用『 source ~/bin/gototmp.sh 』指令, 再次查閱一下你的工作目錄是否正確的進入到 /tmp 了?
[student@localhost bin]$ source gototmp.sh /tmp [student@localhost tmp]$ pwd /tmp
在第二堂課我們曾經使用過 bc 來計算數學的 pi,亦即使用『echo "scale=10; 4*a(1)" | bc -lq』來計算 pi。 而如果需要輸出更正確的 pi 值,可以將 scale 的參數放大,例如『echo "scale=20; 4*a(1)" | bc -lq』來計算。 我們是否能夠使用一個變數,讓該變數帶入腳本後,讓用戶可以與系統對談呢?這時有兩種基本的方法可以達到這個目的:
讀者可以先理解 read 的用法:
[student@localhost ~]$ read -p 'Input your name: ' name1 Input your name: VBird Tsai [student@localhost ~]$ echo ${name1} VBird Tsai
亦即 read 會將使用者輸入的資料變成變數內容,之後就可以輕易的進行變數設定的任務。因此,若需要讓使用者與程式互動來輸入 pi 的計算精確度, 可以寫下如下的腳本:
[student@localhost ~]$ vim ~/bin/mypi.sh #!/bin/bash # Program: # User input a scale number to calculate pi number. # History: # 2015/07/16 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH echo -e "This program will calculate pi value. \n" echo -e "You should input a float number to calculate pi value.\n" read -p "The scale number (10~10000) ? " num echo -e "Starting calculate pi value. Be patient." time echo "scale=${num}; 4*a(1)" | bc -lq [student@localhost ~]$ chmod a+x ~/bin/mypi.sh [student@localhost ~]$ mypi.sh This program will calculate pi value. You should input a float number to calculate pi value. The scale number (10~10000) ? 50 Starting calculate pi value. Be patient. 3.14159265358979323846264338327950288419716939937508 real 0m0.001s user 0m0.000s sys 0m0.002s
此時,只要用戶執行 mypi.sh ,就可以手動輸入 10 到 10000 之間不等的數值,讓系統直接進行運算工作!此外你得要注意, 在 echo 顯示資訊時,可以使用 \t, \n 等方法來處理斷行、[tab] 按鈕的樣式~只是需要加上 -e 的選項才行!你可以『 man echo 』查詢可用的變數!
[student@localhost ~]$ listcmd.sh This shell script will list your command's full path name and permissions. Please input a command name: ls -rwxr-xr-x. 1 root root 166448 5月 12 2019 /usr/bin/ls getfacl: Removing leading '/' from absolute path names # file: usr/bin/ls # owner: root # group: root user::rwx group::r-x other::r-x
在第 8 堂課的課程內容曾經短暫介紹過 shell 內有個名為 ${1} 的變數,即是 shell script 的外帶參數。事實上,外帶參數可以有多個, 相關的『數字變數』有底下的相關性:
/path/to/scriptname opt1 opt2 opt3 opt4
$0 $1 $2 $3 $4
執行的腳本檔名為 ${0} 這個變數,第一個接的參數就是 ${1}。所以,只要在 script 裡面善用 ${1} ,就可以很簡單的立即下達某些指令功能了! 除了這些數字的變數之外,尚有底下這些常見的變數可以在 shell script 內呼叫:
在某些時刻,執行腳本可能是在背景中,因此不可能跟使用者互動 (記得 jobs, fg, bg 的情況下),此時就能夠透過這種外帶參數的方式來執行。 例如我們將 mypi.sh 修改成外帶參數的 mypi2.sh ,讀者可以這樣嘗試:
[student@localhost ~]$ cp ~/bin/mypi.sh ~/bin/mypi2.sh [student@localhost ~]$ vim ~/bin/mypi2.sh #!/bin/bash # Program: # User input a scale number to calculate pi number. # History: # 2015/07/16 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH num=${1} echo -e "This program will calculate pi value. \n" #read -p "The scale number (10~10000) ? " num echo -e "Starting calculate pi value. Be patient." time echo "scale=${num}; 4*a(1)" | bc -lq [student@localhost ~]$ chmod a+x ~/bin/mypi2.sh [student@localhost ~]$ mypi2.sh 50 This program will calculate pi value. Starting calculate pi value. Be patient. 3.14159265358979323846264338327950288419716939937508 real 0m0.001s user 0m0.000s sys 0m0.002s
讀者可以發現,mypi2.sh 將 mypi.sh 內的兩行輸出 (說明程式功能與 read 的功能) 取消,而在不修改其他程式碼的情況下, 讓 num=${1} 來讓精確度使用第一個外帶參數的方式來處理。
[root@station5-237 bin]# listcmd2.sh pwd
This shell script will list your command's full path name and permissions.
-rwxr-xr-x. 1 root root 46840 5月 12 2019 /usr/bin/pwd
getfacl: Removing leading '/' from absolute path names
# file: usr/bin/pwd
# owner: root
# group: root
user::rwx
group::r-x
other::r-x
讀者可以思考 mypi.sh 這個腳本的運作,雖然指定使用者應該要輸入 10~10000 的數值,但是卻沒有在腳本中進行防呆, 因此,當使用者輸入 (1)非為數值的字串及 (2)輸入超過數值範圍時,就可能發生程式誤判的情況,如下:
[student@localhost ~]$ mypi.sh This program will calculate pi value. You should input a float number to calculate pi value. The scale number (10~10000) ? whoami Starting calculate pi value. Be patient. 0 real 0m0.001s user 0m0.001s sys 0m0.001s [student@localhost ~]$ mypi.sh This program will calculate pi value. You should input a float number to calculate pi value. The scale number (10~10000) ? <==這裡直接按下 enter 就好 Starting calculate pi value. Be patient. (standard_in) 1: syntax error real 0m0.001s user 0m0.000s sys 0m0.002s
此時就會發生不可預期的錯誤。讀者在設計程式腳本時,應該就用戶可能會輸入的字元或通常的運作方式進行分析, 先設計好防呆,在程式碼的運作上比較不容易出問題。想要達成這種防呆的機制,需要用到條件判斷式的支援,一般 shell script 條件判斷的語法為:
if [ 條件判斷式 ]; then 當條件判斷式成立時,可以進行的指令工作內容; fi <==將 if 反過來寫,就成為 fi 啦!結束 if 之意!
相關條件設定的方式已經在第八堂課談過,請自行前往參閱。若有多重條件判斷,則使用下列方式:
# 一個條件判斷,分成功進行與失敗進行 (else) if [ 條件判斷式 ]; then 當條件判斷式成立時,可以進行的指令工作內容; else 當條件判斷式不成立時,可以進行的指令工作內容; fi
如果考慮更複雜的情況,則可以使用這個語法:
# 多個條件判斷 (if ... elif ... elif ... else) 分多種不同情況執行 if [ 條件判斷式一 ]; then 當條件判斷式一成立時,可以進行的指令工作內容; elif [ 條件判斷式二 ]; then 當條件判斷式二成立時,可以進行的指令工作內容; else 當條件判斷式一與二均不成立時,可以進行的指令工作內容; fi
如果考慮兩個以上的條件混合執行時,就需要使用 -a 或 -o 的協助。
# 兩個條件都要成立才算成立的情況: if [ 條件判斷式一 -a 條件判斷二 ]; then 兩個條件都成立,這時才執行 (and 的概念) fi # 兩個條件中,任何一個條件成立都算 OK 的情況: if [ 條件判斷式一 -o 條件判斷二 ]; then 隨便哪一個條件成立,都可以執行 (or 的概念) fi
事實上,還有另一種複合條件判斷的指令語法,有點像這樣:
# 兩個條件都要成立才算成立的情況: if [ 條件判斷式一 ] && [ 條件判斷二 ]; then 兩個條件都成立,這時才執行 (and 的概念) fi # 兩個條件中,任何一個條件成立都算 OK 的情況: if [ 條件判斷式一 ] || [ 條件判斷二 ]; then 隨便哪一個條件成立,都可以執行 (or 的概念) fi
以上面的語法來補足 mypi.sh 的防呆,大致防呆的思考可以是:
大致的防呆流程就像上面所敘述,接下來讀者們可以使用語法來將這些流程加入 mypi.sh:
[student@localhost ~]$ vim ~/bin/mypi.sh #!/bin/bash # Program: # User input a scale number to calculate pi number. # History: # 2015/07/16 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH echo -e "This program will calculate pi value. \n" echo -e "You should input a float number to calculate pi value.\n" read -p "The scale number (10~10000) ? " num # 判斷是否有非為數字的字元,若有,則會出現,若無則是空白 checking=$( echo ${num} | grep '[^0-9]' ) if [ "${num}" == "" -o "${checking}" != "" ]; then num=20 fi if [ "${num}" -le 10 ]; then num=10 elif [ "${num}" -ge 10000 ]; then num=10000 fi echo "Use scale number: ${num}" echo -e "Starting calculate pi value. Be patient." time echo "scale=${num}; 4*a(1)" | bc -lq
接下來請執行數次 mypi.sh ,並分別輸入不同的資料 (Enter, 文字, 小於 10 的數字, 大於 10000 的數字等等), 以確認自己的處理方式應為可行。
若讀者只想讓 mypi.sh 的操作者體驗一下 pi 的計算,因此只想給予 20, 100, 1000 三個數值, 當使用者不是輸入此類數值,則告知對方僅能輸入這三個數值。若以 if ... then 的方式來說,需要填寫的判斷式稍嫌多了些。 此時可以使用 case ... esac 來做設計。
case $變數名稱 in <==關鍵字為 case ,還有變數前有錢字號 "第一個變數內容") <==每個變數內容建議用雙引號括起來,關鍵字則為小括號 ) 程式段 ;; <==每個類別結尾使用兩個連續的分號來處理! "第二個變數內容") 程式段 ;; *) <==最後一個變數內容都會用 * 來代表所有其他值 不包含第一個變數內容與第二個變數內容的其他程式執行段 exit 1 ;; esac <==最終的 case 結尾!『反過來寫』思考一下!
請使用上述的語法,搭配僅能輸入 20, 100, 1000 三個數值來撰寫 mypi3.sh 腳本:
[student@localhost ~]$ cp ~/bin/mypi.sh ~/bin/mypi3.sh [student@localhost ~]$ vim ~/bin/mypi3.sh #!/bin/bash # Program: # User input a scale number to calculate pi number. # History: # 2015/07/16 VBird First release PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH echo -e "This program will calculate pi value. \n" echo -e "You should input a float number to calculate pi value.\n" read -p "The scale number (20,100,1000) ? " num case ${num} in "20") echo "Your input is 20" ;; "100") echo "Your input is 100" ;; "1000") echo "Your input is 1000" ;; *) echo "You MUST input 20|100|1000" echo "I stop here" exit 0 ;; esac echo -e "Starting calculate pi value. Be patient." time echo "scale=${num}; 4*a(1)" | bc -lq [student@localhost ~]$ chmod a+x ~/bin/mypi3.sh [student@localhost ~]$ mypi3.sh This program will calculate pi value. You should input a float number to calculate pi value. The scale number (20,100,1000) ? 30 You MUST input 20|100|1000 I stop here [student@localhost ~]$ mypi3.sh This program will calculate pi value. You should input a float number to calculate pi value. The scale number (20,100,1000) ? 100 Your input is 100 Starting calculate pi value. Be patient. 3.141592653589793238462643383279502884197169399375105820974944592307\ 8164062862089986280348253421170676 real 0m0.003s user 0m0.003s sys 0m0.000s
這樣就可以縮減使用者輸入的參數了!
事實上,一般讀者只要瞭解基礎型的正規表示法大概就已經相當足夠了,不過,某些時刻為了要簡化整個指令操作, 瞭解一下使用範圍更廣的延伸型正規表示法的表示式會更方便呢!舉個簡單的例子,一般我們要去除資訊內的 (1)空白行與 (2)註解行時, 可能需要使用兩次基本正規表示法,如下所示:
[student@localhost ~]$ grep -v '^$' /etc/crontab | grep -v '^#'
延伸型正規表示法,可以透過『群組』的功能,將需要擷取的關鍵字寫在一起,整個指令就會變得更清爽:
[student@localhost ~]$ egrep -v '^$|^#' /etc/crontab
grep 支援基本正規表示法,若要使用到延伸正規表示法,就得要使用 egrep 這個指令來替換才行。
熟悉了正規表示法之後,到這個延伸型的正規表示法,你應該也會想到,不就是多幾個重要的特殊符號嗎? 是的~所以,我們就直接來說明一下,延伸型正規表示法有哪幾個特殊符號?
RE 字符 | 意義與範例 |
+ | 意義:重複『一個或一個以上』的前一個 RE 字符 範例:搜尋 (god) (good) (goood)... 等等的字串。 那個 o+ 代表『一個以上的 o 』所以,底下的執行成果會將第 1, 9, 13 行列出來。 egrep -n 'go+d' regular_express.txt |
? | 意義:『零個或一個』的前一個 RE 字符 範例:搜尋 (gd) (god) 這兩個字串。 那個 o? 代表『空的或 1 個 o 』所以,上面的執行成果會將第 13, 14 行列出來。 有沒有發現到,這兩個案例( 'go+d' 與 'go?d' )的結果集合與 'go*d' 相同? 想想看,這是為什麼喔! ^_^ egrep -n 'go?d' regular_express.txt |
| | 意義:用或( or )的方式找出數個字串 範例:搜尋 gd 或 good 這兩個字串,注意,是『或』! 所以,第 1,9,14 這三行都可以被列印出來喔!那如果還想要找出 dog 呢? egrep -n 'gd|good' regular_express.txt |
() | 意義:找出『群組』字串 範例:搜尋 (glad) 或 (good) 這兩個字串,因為 g 與 d 是重複的,所以, 我就可以將 la 與 oo 列於 ( ) 當中,並以 | 來分隔開來,就可以啦! egrep -n 'g(la|oo)d' regular_express.txt |
()+ | 意義:多個重複群組的判別 範例:將『AxyzxyzxyzxyzC』用 echo 叫出,然後再使用如下的方法搜尋一下! echo 'AxyzxyzxyzxyzC' | egrep 'A(xyz)+C'上面的例子意思是說,我要找開頭是 A 結尾是 C ,中間有一個以上的 "xyz" 字串的意思~ |
以上這些就是延伸型的正規表示法的特殊字元。另外,要特別強調的是,那個 ! 在正規表示法當中並不是特殊字元, 所以,如果你想要查出來檔案中含有 ! 與 > 的字行時,可以這樣:
[student@localhost ~]$ grep -n '[!>]' regular_express.txt
另外,延伸正規表示法對於一般使用者來說,大致上最重要的就是群組 (|) 這種用法~所以,目前只要知道 egrep 這個指令的使用時機, 以及透過 | 來分隔要同時擷取的關鍵字即可!
作業硬碟一般操作說明:
作業當中,某些部份可能為簡答題~若為簡答題時,請將答案寫入 /home/student/ans.txt 當中,並寫好正確題號,方便老師訂正答案。 請注意,檔名寫錯將無法上傳!
請使用 root 的身份進行如下實做的任務。直接在系統上面操作,操作成功即可,上傳結果的程式會主動找到你的實做結果。
[root@localhost ~]# getmesg .... May 10 19:57:16 localhost chronyd[771]: System clock was stepped by -28797.191834 seconds May 10 19:57:16 localhost chronyd[771]: System clock TAI offset set to 37 seconds May 10 19:57:16 localhost rsyslogd[748]: imjournal: journal files changed, reloading... May 10 19:57:16 localhost rsyslogd[748]: imjournal: journal files changed, reloading... .... # 月份名稱會出現在行首!
[root@localhost ~]# chsel 0 # 此時 /etc/selinux/config 內的那行會被改成 SELINUX=permissive [root@localhost ~]# chsel 1 # 此時 /etc/selinux/config 內的那行會被改成 SELINUX=enforcing
[student@localhost ~]$ cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 [student@localhost ~]$ chupper 127.0.0.1 LOCALHOST LOCALHOST.LOCALDOMAIN LOCALHOST4 LOCALHOST4.LOCALDOMAIN4 ::1 LOCALHOST LOCALHOST.LOCALDOMAIN LOCALHOST6 LOCALHOST6.LOCALDOMAIN6 # 可以發現英文全部變大寫字元了!
[student@localhost ~]$ myprocess
PID USER CPU TT COMMAND
1 root - ? /usr/lib/systemd/systemd rhgb --switched-root --system --deserialize 31
2 root - ? [kthreadd]
3 root - ? [rcu_gp]
....
[student@localhost ~]$ mydate.sh
2023/05/10
20:33:10
1683721990
May 2023
Mo Tu We Th Fr Sa Su
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
[student@localhost ~]$ listcmd.sh Usage: /usr/local/bin/listcmd.sh cmd_name [student@localhost ~]$ echo $? 2
[student@localhost ~]$ listcmd.sh passwd /usr/bin/passwd -rwsr-xr-x. 1 root root 32656 May 15 2022 /usr/bin/passwd getfacl: Removing leading '/' from absolute path names # file: usr/bin/passwd # owner: root # group: root # flags: s-- user::rwx group::r-x other::r-x [student@localhost ~]$ echo $? 0
[student@localhost ~]$ listcmd.sh shadow ----------. 1 root root 1159 Feb 16 18:19 /etc/shadow [student@localhost ~]$ echo $? 0 [student@localhost ~]$ listcmd.sh nonfile no this filename [student@localhost ~]$ echo $? 10
[student@localhost ~]$ myheha hehe I am haha [student@localhost ~]$ myheha haha You are hehe [student@localhost ~]$ myheha nono Usage: /usr/local/bin/myheha hehe|haha
[student@localhost ~]$ yourbday.sh Usage: /usr/local/bin/yourbday.sh YYYY-MM-DD [student@localhost ~]$ yourbday.sh 2001-03-53 invalid date format [student@localhost ~]$ yourbday.sh 2001-03-21 You are 22.13 old
[student@localhost ~]$ examcheck ok Yes! You are right! [student@localhost ~]$ examcheck false So sad...Your answer is wrong... [student@localhost ~]$ examcheck qqq Usage: examcheck ok|false
作業結果傳輸:請以 root 的身分執行 vbird_book_check_unit 指令上傳作業結果。 正常執行完畢的結果應會出現【XXXXXX_aa:bb:cc:dd:ee:ff_unitNN】字樣。若需要查閱自己上傳資料的時間, 請在作業系統上面使用瀏覽器查詢: http://192.168.251.254 檢查相對應的課程檔案。 相關流程請參考: vbird_book_check_unit