第 9 堂課:正規表示法與 shell script 初探

http://linux.vbird.org/linux_basic_train/unit09.php

9.1:正規表示法的應用

9.1.1:grep 指令的應用

輸入 df 後,將 tmpfs 相關的那幾行取消,讓螢幕僅輸出一般的檔案系統,方便查閱。 (註:man grep 找 invert "反向" 搜尋的關鍵字)

[root@mail ~]# df | grep -v tmpfs
Filesystem              1K-blocks    Used Available Use% Mounted on
/dev/mapper/centos-root  14034944 1573508  12461436  12% /
/dev/sda1                 1038336  190292    848044  19% /boot

9.1.2:正規表示法的符號意義

找出 /etc/services 內含 http 關鍵字的那幾行

[root@mail ~]# grep -n http /etc/services

承上,若僅須『開頭含有 http 』字樣的那幾行?

[root@mail ~]# grep -n ^http /etc/services

承上,若僅須『開頭含有 http 或 https 』字樣的那幾行?

[root@mail ~]# grep -n ^https* /etc/services

承上,若僅須『開頭含有 http 或 https 』字樣之外,且後面僅能接空白字元或 [tab] 字元的那幾行?

[root@mail ~]# grep -n ^https*[[:space:]] /etc/services

承上,若僅須『開頭含有 http 且後續接有 80 』字樣的那幾行

[root@mail ~]# grep -n ^https*[[:space:]]*80 /etc/services

找出 /etc/services 內含有星號 (*) 的那幾行

[root@mail ~]# grep -n \* /etc/services

找出 /etc/services 內含有星號,且星號前為英文(不論大小寫)的那幾行

[root@mail ~]# grep -n [[:alpha:]][\*] /etc/services

找出 /etc/services 含有一個數字緊鄰一個大寫字元的那幾行

[root@mail ~]# grep -n [[:digit:]][[:upper:]] /etc/services

找出 /etc/services 開頭是一個數字緊鄰一個大寫字元的那幾行

[root@mail ~]# grep -n ^[[:digit:]][[:upper:]] /etc/services

使用 find /etc 找出檔名,並找出結尾含有『 .conf 』的檔名資料

[root@mail ~]# grep -n .conf$ /etc/services

承上,且含有『大寫字元或數字』檔名在內的那幾個

[root@mail ~]# grep -n .conf$ /etc/services | grep [[:upper:]] | grep [[:digit:]]

9.1.3:sed 工具的使用

找出 /etc/passwd 裡面,結尾是 bash 的那幾行

[root@mail ~]# grep bash$ /etc/passwd

承上,透過 sed 將 /bin/bash 改成 /sbin/nologin 顯示到螢幕上

[root@mail ~]# grep bash$ /etc/passwd | sed 's/\/bin\/bash/\/sbin\/nologin/g'

承上,透過 tr 這個指令,將全部的英文都變成大寫字元

[root@mail ~]# grep bash$ /etc/passwd | tr '[:lower:]' '[:upper:]'

9.2.1:基礎 shell script 的撰寫與執行

如何直接以 bash 或 sh 去執行這隻腳本 (指的是直接用 bash 指令去執行,而不是執行 myid.sh)?

sh myid.sh

承上,執行過程中,如果還需要輸出程式碼之後才執行,可以加上哪個選項?(此功能相當有用!可用以檢測程式碼的錯誤, debug)

看不懂這題

若需要直接輸入 myid.sh 就能執行時,需要有什麼設定 (包括權限、路徑等等的資料) ?

myid.sh所在的目錄要在PATH裡面
myid要有x權限

如何使用絕對路徑來執行?

/home/student/bin/myid.sh

若你剛好在工作目錄下看到這個腳本,但你不確定工作目錄有沒有在 PATH 環境中,該如何下達指令執行該腳本?

直接sh ./myid.sh 或是 myid.sh

為何 alias 的結果沒有輸出?若執行該腳本時還要輸出目前的 alias ,該如何執行?為什麼?

要用source myid.sh

目前不知道為什麼...只知道source是用主線程執行,sh是用子線程執行,但為何主線程跟子線程執行結過有差異,目前還是想不通

9.2.2:shell script 的執行環境

例題:你在操作 Linux 系統的過程中,可能會切換到許多不同的 shell (例如 bash 轉到 csh 等等)。不過這些操作環境中,均需要使用到底下的變數, 且這些變數是在有需要時才載入的 (不是寫入到 .bashrc),因此需要額外撰寫成 myenv.sh。內容需要:

  1. 設計 MYIP 的變數為目前系統的 IP (假設網路卡為 eth0 時)
  2. 設計 mywork 的變數為指定到 /usr/local/libexec 目錄中
  3. 設計 megacli 的變數為 /opt/mega/cli/command 這個指令(此指令並不存在,僅作為範例用)
  4. 設計完畢之後,若要使用這個檔案內的資料,該如何執行?
#1 2 3

---------------------------------------------------------
MYIP=$(ip addr show | grep "inet\b" | awk '{print $2}')
mywork=/usr/local/libexec
megacli=/opt/mega/cli/command

#4
chmod a+x myenv.sh
myenv.sh

9.2.3:以對談式腳本及外帶參數計算 pi

例題:建立一個名為 /usr/local/bin/listcmd.sh 的腳本,該腳本可以完成底下的各項工作:

  1. 第一行一定要宣告 shell
  2. 顯示出這個腳本的目的 (中英文均可,例如: This shell script will list your command's full path name and permissions.)
  3. 開始透過 read 讓用戶輸入指令名稱
  4. 透過上一步驟取得指令名稱後,透過 which 找到這個指令的完整路徑
  5. 利用 ls -l 列出這個指令的完整權限
  6. 利用 getfacl 列出這個指令的完整權限
  7. 離開 shell script,並回傳 0 的數值。

最後將該指令的權限修訂成全部成員均可執行,並執行一次確認狀態。

vim listcmd.sh

---------------------------------------------------------------------------------
#!/bin/bash
echo -e "This shell script will list your command's full path name and permissions."
read -p "Please Input Command: " command
ls -l $(which ${command})
getfacl $(which ${command})
exit 0;
---------------------------------------------------------------------------------

chmod a+x listcmd.sh

例題:建立一個名為 /usr/local/bin/listcmd2.sh 的腳本,該腳本可以完成底下的各項工作:

  1. 第一行一定要宣告 shell
  2. 顯示出這個腳本的目的 (中英文均可,例如: This shell script will list your command's full path name and permissions.)
  3. 取得第一個外帶參數的內容
  4. 透過上一步驟取得指令名稱後,透過 which 找到這個指令的完整路徑
  5. 利用 ls -l 列出這個指令的完整權限
  6. 利用 getfacl 列出這個指令的完整權限
  7. 離開 shell script,並回傳 0 的數值。

最後將該指令的權限修訂成全部成員均可執行,並執行一次確認狀態。

cp listcmd.sh listcmd2.sh

------------------------------------------------------------------------------------
#!/bin/bash
echo -e "This shell script will list your command's full path name and permissions."

ls -l $(which ${1})
getfacl $(which ${1})
exit 0;
------------------------------------------------------------------------------------

9.2.4:透過 if .. then 設計條件判斷

請透過相同的方法來修改 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}

if [ "${num}" == "" ]; then                             # check empty number
        echo "You must input a number..."
        echo "I will use this number '20' to calculate pi"
        num=20
else
        checking="$(echo ${num} | grep '[^0-9]')"       # check if any non-number char
        if [ "${checking}" != "" ]; then                # check non-number value
                echo "You must input number..."
                echo "I will use this number '20' to calculate pi"
                num=20
        fi
fi
if [ "${num}" -lt 10 ]; then
        echo "I will use this number '10' to calculate pi"
        num=10
elif [ "${num}" -gt 10000 ]; then
        echo "I will use this number '10000' to calculate pi"
        num=10000
fi

echo -e "This program will calculate pi value. \n"
echo -e "Starting calculate pi value.  Be patient."
time echo "scale=${num}; 4*a(1)" | bc -lq

9.2.5:以 case .. esac 設計條件判斷

透過 case ... esac 的方法,修改 mypi2.sh 變成 mypi4.sh,以外帶參數的方式,讓 mypi4.sh 只能支援 20|100|1000 的數值, 若使用者外帶參數不是這三個,則顯示『 Usage: mypi4.sh 20|100|1000 』的螢幕提示,否則就直接計算 pi 值並輸出結果。

cp mypi2.sh mypi4.sh
vim mypi4.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}

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 "This program will calculate pi value. \n"
echo -e "Starting calculate pi value.  Be patient."
time echo "scale=${num}; 4*a(1)" | bc -lq

-----------------------------------------------

mypi4.sh 300

-----------------------------------------------
You MUST input 20|100|1000
I stop here

mypi4.sh 20 

-----------------------------------------------
Your input is 20
This program will calculate pi value. 

Starting calculate pi value.  Be patient.
3.14159265358979323844

real    0m0.002s
user    0m0.000s
sys    0m0.000s

9.3:課後練習操作

分析『本日』登錄檔資訊的相關設定,重點在實做與練習正規表示法:(將答案寫入 /root/ans09.txt 中)

  1. 先解析一下 /var/log/messages 的內容中,每條訊息的最前面紀錄的日期,如何使用 date 搭配選項來輸出一樣的字串?(你或許需要知道搭配語系輸出)
  2. 設定一個變數名稱為 logday ,讓 logday 的內容為剛剛查詢到的日期格式
  3. 如何透過 grep 搭配 ${logday} 等方式,將 /var/log/messages 的資訊中,跟今天有關的日期取出來到螢幕上查閱?(注意日期要出現在行首喔!)
  4. 承上,上述的輸出結果中,我不想要關鍵字 dbus-daemon 與 dbus[數字] 的內容又該如何處理?
  5. 我想要透過一串指令,直接將 /etc/selinux/config 檔案內,行首出現『SELINUX=??? 』的那一行(一整行喔)資料,強制替換成『SELINUX=enforcing』, 且直接修改該檔案,該如何處理?
  6. 我要把 /etc/hosts 內容全部轉成大寫字元後,轉存到 /dev/shm/upperhosts 檔案,指令該如何處理?
#1 2 3 4
Docker Centos沒有/var/log/messages檔案,不過猜測是要考grep跟正規表示法的複合搭配

#5
Docker Centos沒有/etc/selinux/config檔案,不過猜測要考grep跟sed指令的複合搭配

#6
cat /etc/hosts | tr '[:lower:]' '[:upper:]' > /dev/shm/upperhosts

建立一隻名為 /usr/local/bin/myprocess 的腳本,腳本內容主要為:

  1. 第一行一定要宣告 shell 為 bash 才行;
  2. 主要僅執行『 /bin/ps -Ao pid,user,cpu,tty,args 』
  3. 這隻腳本必須要讓所有人都可以執行才行!
vim /usr/local/bin/myprocess

-----------------------------------------
#!/bin/bash
/bin/ps -Ao pid,user,cpu,tty,args
-----------------------------------------

chmod a+x /usr/local/bin/myprocesscal

寫一隻名為 /usr/local/bin/mydate.sh 的腳本,執行後可以輸出如下的資料:

  1. 第一行一定要宣告 shell 才對!
  2. 以 西元年/月/日 顯示出目前的日期
  3. 以 小時:分鐘:秒鐘 顯示出目前的時間
  4. 輸出從 1970/01/01 到目前累計的秒數
  5. 列出這個月的月曆,且依據台灣的習慣,輸出時,以星期一為一週的開始。
  6. 這隻腳本必須要讓所有人都可以執行才行!
vim /usr/local/bin/mydate.sh

------------------------------------------------
#!/bin/bash
date +'%Y/%m/%d'
date +'%H/%M/%S'
date +'%s'
cal -m
------------------------------------------------
chmod a+x /usr/local/bin/mydate.sh

寫一隻 /usr/local/bin/listcmd.sh 的腳本,該腳本執行後,會告知底下相關的事宜:

  1. 腳本的執行方式為『 listcmd.sh passwd 』,其中 passwd 可以使用任何檔名來取代
  2. 第一行一定要宣告 shell
  3. 先顯示出這個腳本的目的 (中英文均可,例如: This shell script will list your command's full path name and permissions.)
  4. 判斷是否有外帶參數,若沒有外帶參數,請 (1)螢幕顯示『 Usage: ${0} cmd_name 』,(2)並以回傳值 2 離開程式
  5. 使用『 which ${1} 2> /dev/null 』的結果,判斷該字串是否為指令
  6. 若該字串為指令,則依序輸出:
    • 輸出指令的完整路徑
    • 用 ls -l 列出這個指令的完整權限
    • 利用 getfacl 列出這個指令的完整權限
    • 離開 shell script,並回傳 0 的數值。
  7. 若該字串不為指令,則使用 locate 後面加 /${1}$ 的正規表示法 (locate 要支援正規表示法,必須要輸入特定的選項 請自行 man locate 查到正確的選項支援),然後依據 locate 之後的回傳值處理後續工作
    • 若回傳值 (為 0) 顯示該字串其實具有相同的檔名,則使用 ls -ld 將檔名全部列出,然後以回傳值 0 離開程式
    • 若回傳值 (不為 0) 顯示該字串並不為檔名,則顯示找不到這個檔名,然後以回傳值 10 離開程式
#!/bin/bash

echo -e "This shell script will list your command's full path name and permissions."
command=${1}

if [ "${command}" == "" ]; then
    echo -e "Usage:${0} cmd_name"
    exit 2;
fi

commandPath=$(which $command 2> /dev/null)
commandStatus=$(echo $?)
if [ "${commandStatus}" == "0" ]; then
    echo -e $commandPAth
    ls -l $commandPath
    getfacl $commandPath
    exit 0;
else
   locateResult=$(locate -r /${command}$)
   locateStatus=$(echo $?)
   if [ "${locateStatus}" == "0" ]; then
        ls -ld ${locateResult}
        exit 0;
   else
        echo -e "Can't find file"
        exit 10;
   fi
fi

寫一隻名為 /usr/local/bin/myheha 的腳本,這隻腳本的執行結果會這樣:

  1. 腳本內第一行一定要宣告 shell 為 bash
  2. 當執行 myheha hehe 時,螢幕會輸出『 I am haha 』
  3. 當執行 myheha haha 時,螢幕會輸出『 You are hehe 』
  4. 當外帶參數不是 hehe 也不是 haha 時,螢幕會輸出『 Usage: myheha hehe|haha 』
#!/bin/bash
case ${1} in
"hehe")
    echo -e "I am haha"
    ;;
"haha")
    echo -e "you are hehe"
    ;;
*)
    echo -e "Usage:myheha hehe|haha"
    ;;
esac

寫一隻判斷生日的腳本,名稱為 /usr/local/bin/yourbday.sh,內容為:

  1. 腳本內第一行一定要宣告 shell 為 bash
  2. 指令執行的方式為『 yourbday.sh YYYY-MM-DD 』
  3. 當使用者沒有輸入外帶參數時,螢幕顯示『 Usage: yourbday.sh YYYY-MM-DD 』,並且離開程式
  4. 以正規表示法的方式來查詢生日的格式是否正常,若不正常,重新顯示上面的訊息,並且離開程式
  5. 以 date --date="YYYY-MM-DD" +%s 的回傳值確認時間格式是否正確?若不正確請顯示『 invalid date 』後,離開程式
  6. 分別取得生日與現在的累積秒數,根據兩者的差異,同時假設一年 365.25 天,然後:
    • 如果生日比現在的總累積秒數還要大,代表來自未來,請輸出『You are not a real human..』,之後離開程式
    • 如果所有問題都排除了,那請搭配 bc 來顯示出你的歲數,歲數計算到小數點第二位, 例如『 You are 22.35 years old 』的樣式。
#!/bin/bash
command1=${1}
echo $command1
if [ "$command1" == "" ]; then
    echo -e "Usage: yourbday.sh YYYY-MM-DD";
    exit 0;
fi

birthdayTime=$(date --date=$command1 +"%s")
dateResult=$(echo $?)
if [ "$dateResult" -ne "0" ]; then
    echo "Invalid date"
    exit 0;
fi
currentTime=$(date +"%s")
if [ $currentTime -gt $birthdayTime ]; then
    echo "(${currentTime} - ${birthdayTime})/86400/365.24" | bc -lq
else
    echo -e "Your are not a real human..."
fi

有一隻腳本名為: /usr/local/sbin/examcheck ,當執行時,應該要出現如下的畫面, 但是程式開發人員寫錯了某些地方,請你將應該有問題的程式碼訂正,使得腳本得以展示如下的結果:

  1. 執行 examcheck ok 時,顯示『Yes! You are right!』
  2. 執行 examcheck false 時,顯示『So sad... your answer is wrong..』
  3. 執行 examcheck otherword 時,顯示『 Usage: examcheck ok|false 』(otherword 為任意字元,隨便不是 ok 與 false 的其他字元之意)
#!/bin/bash
case ${1} in
"ok")
    echo -e "Yes! You are right"
    ;;
"false")
    echo -e "So sad... your answer is wrong..."
    ;;
*)
    echo -e "Usage:examcheck ok|false"
    ;;
esac

results matching ""

    No results matching ""