Skip to content

Scripting

腳本

環境

在執行中,shell會透過PATH環境變量來尋找指令,有兩種方法可以解決找不到命令的問題

  • 將shell腳本放到PATH環境變量中
  • 利用相對或絕對路徑來引用腳本
  • echo 顯示訊息,可以使用單引號'或雙引號"界定字符串
    • -n 將字符串與命令顯示在同一列中
  • $ 引用環境變量
    • 反斜線\允許shell腳本按照字面顯示$

自定義變量

  • shell允許用戶在腳本中自定義和使用自己的變量,但變量、等號與值中間 不能有空格
  • 引用變量時加$,對變量賦值時不用加
  • 從命令輸出中提取訊息並賦予變量

    • (``)反引號
    • $() 格式 若date為命令,則
      testing=`date`
      #或是
      testing=$(date)
      

    輸出日期例子

    #!/bin/bash
    #copy the /usr/bin directory listing to a log file
    today=$(date +%y%m%d)
    ls /usr/bin -al > log.$today
    

    注意:命令替換子shell中執行,無法使用腳本中的變量,但如果以不加路徑的方式執行則不會創建子shell


    基本操作

  • >, >> ,< 將命令作為輸入/輸出

    $ date >> test.log
    $ command < inputfile
    

  • |:將命令傳給另一個命令,這個過程稱為 piping

    ##usage
    $ command1 | command2
    ##Example
    $ rpm -qa | sort | more
    

  • EOF:定義了重定向數據的起止

  • $?:linux提供的專門變量,用來保存最後一個已執行命令的退出狀態碼

    一般狀態碼定義如下

    狀態碼 描述
    0 命令成功結束
    1 一般未知錯誤
    2 不適合的shell命令
    126 命令無法執行
    127 沒找到命令
    128 無效的退出參數
    128+x 與linux信號x相關的嚴重錯誤
    130 透過ctrl + c終止的命令
    255 正常範圍以外的退出狀態碼
  • exit

    默認狀態下,以腳本最後一個命令的退出狀態碼退出,也可以自己指定退出狀態碼

    使用時,記得退出最大狀態碼僅有255,超過會以255的餘數作為回傳值

  • /dev/null 被稱為黑洞,送往這的東西都會有去無回 查看下列程式碼

    if (which cat &> /dev/null)
    then
        echo "You have cat installed"
    fi
    
    將標準輸出與標準錯誤訊息重定向(&>)至/dev/null,使腳本輸出簡潔許多 > 注意:&> 會重定向標準輸出與標準錯誤,而>僅重定向標準輸出

  • /dev/zero 充滿著無限個0 bit的文件


數學運算

  • expr命令:最一開始的數學表達式命令,但也特別笨拙
  • 方括號[]:更簡單的方法執行數學運算 要將數學運算賦給變量,可以使用$[]var=$[operation] > bash shell一般只支援整數運算

浮點數

  • bc:bash 計算器

    實際上是一種編程語言,允許在命令行輸入浮點數表達

運算需要先設置scale,顯示小數點後幾位,預設為0 * quit:離開bc

在腳本中使用bc的方法為$ variable=$(echo "options; expression" | bc) in shell script

var1=$(echo "scale=4;3.44 / 5 "| bc)
echo "the answer is $var1"
要大量使用浮點數運算,可以利用在命令列中重定向的方式
varable=$(bc << EOF
options
statements
expressions
EOF
)
EOF字符串標定了重定向給bc命令的起止


條件控制

if-then-else語句

if command
then
    commands

elif command
then
    commands

else
    commands
fi
bash語句會執行if之後的命令,如果該命令的退出狀態碼為0,位於then的命令才會執行

if-then腳本有另一種形式

if command; then
    commands
fi
透過把分號(;)放在代求值的命令尾部,可以將then語句寫在同一行 緊跟在elif後的else是屬於elif的,不屬於之前的if-then語句

  • test命令 test命令可以在if-then語句中測試不同的條件,如果test中的條件成立,其會退出並返回狀態0 格式如下:test condition condition是test要測試的一系列參數與值,用在if-then時會看起來如下
    if test condition
    then
        commands
    fi
    
  • bash shell提供另一種條件測試方式,使用[]替代test命令

    注意 :第一個方括號之後與第二個方括號之前 必須 留有空格

    if [ condition ]
    then
        commands
    fi
    

測試可以判斷3種條件

  • 數值比較
  • 字符串比較
  • 文件比較

數值比較

比較 描述
n1 -eq n2 檢查 n1 是否等於 n2
n1 -ge n2 檢查 n1 是否大於或等於 n2
n1 -gt n2 檢查 n1 是否大於 n2
n1 -le n2 檢查 n1 是否小於或等於 n2
n1 -lt n2 檢查 n1 是否小於 n2
n1 -ne n2 檢查 n1 是否不等於 n2

字符串比較

比較 描述
str1 = str2 檢查 str1 與 str2 是否相同
str1 != str2 檢查 str1 與 str2 是否不同
str1 < str2 檢查 str1 是否小於 str2
str1 > str2 檢查 str1 是否大於 str2
-n str1 檢查 str1 的長度是否不為0
-z str2 檢查 str1 的長度是否為0

在比較大小時,是使用每個字符的Unicode編碼值

使用><時必須轉義,否則shell會將其視為重定義符,將字串當作文件名
要解決這問題,需要使用反斜線( )正確轉義

if [ $str1 > $str2 ]  #( wrong )
if [ $str1 \> $str2 ] #( correct )
then
   commands
if

文件比較

這些測試條件允許在shell腳本中檢查文件系統的文件

比較 描述
-d file 檢查 file 是否存在且為目錄
-e file 檢查 file 是否存在
-f file 檢查 file 是否存在且為文件
-r file 檢查 file 是否存在且可讀
-s file 檢查 file 是否存在且非空
-w file 檢查 file 是否存在且可寫
-x file 檢查 file 是否存在且可執行
-O file 檢查 file 是否存在且屬當前用戶所有
-G file 檢查 file 是否存在且默認組與當前用戶相同
file1 -nt file2 檢查 file1 是否比 file2
file1 -ot file2 檢查 file1 是否比 file2

1. 檢查目錄 -d會檢查指定目錄是否存在系統中,在文件寫入前或是切換到某目錄,先測試一下總是好事

jump_dir=/home/Torfa

if [ -d $jump_dir ]
then
    echo "dir exist, jump to target"
    cd jump_dir
else
    echo "dir not exist"
fi

2. 檢查對象是否存在 -e允許在使用文件或目錄前檢查其是否存在

file_name="sofa"
if [ -e $jump_dir/$filename ]
then
    echo "ok on the file, uploading content"
    date >> $jump_dir/$filename
else
    echo "file not found"
fi

3. 檢查文件 確定指定目標為文件,就必須使用-f測試

object=$HOME
if [ -f $object ]
then
    echo "$object is a file"
else
    echo "$object is a directory"
fi
4. 檢查文件是否可讀 嘗試從文件讀取數據前,最好先使用-r確認文件是否可讀
pwfile=/etc/shadow
if [ -r $pwfile]
then
    echo "Displaying end of file"
    tail $pwfile
else
    echo "sorry, the $pwfile file does not exist"
fi

5. 檢查空文件 檢查文件是否為空,尤其是當你不想刪除非空文件時

file_name=$HOME/sentinel
if [ -s $file_name ]
then
    echo "the $file_name file has data in it"
else
    echo "the $file_name file is empty"
fi

6. 檢查是否可寫 -w檢查文件是否可寫

7. 檢查是否可執行 -x檢查文件是否可寫

8. 檢查所有權 -O檢查執行者是否為文件屬主

複合條件

if-then語句允許使用布林邏輯將測試條件組合起來 * [ condition1 ] && [ condition2 ] * [ condition1 ] || [ condition2 ]

使用單括號

單括號 ( )允許if語句中使用子shell,使用格式(command),在bash shell執行command前,會先創建子shell,成功結束則返回0

當在if-then中使用進程列表,可能會出現意料之外的結果,哪怕進程列表最後一個命令以外的其他命令全失敗,子shell仍會返回0

使用雙括號

雙括號 命令允許在比較過程中使用高級數學表示式(基本上跟C++相同)

val=10
if (( val ** 2 >90 ))
then
    (( val-- ))
    echo "value is $val"
fi

使用雙方括號

雙方括號 命令提供了針對字符串比較的高級特性

雙方括號在bash中運行良好,但要注意不是所有的shell都支援雙方括號

if [ [ $BASH_VERSION == 5.* ] ]
then
    echo "you are using the Bash Shell version 5 series"
fi

case 命令

有了case命令,就無需再寫大量的if-elif-else語句,case會採用列表格式來檢查變量的多個值

case variable in
pattern1 | pattern2) commands1;;
pattern3) commands2;;
*) default commands;;
esac
範例
case $USER in
rich | christime) 
    echo "Welcome $USER"
    echo "Please enjoy your visit";;

babara | tim) 
    echo "Hi there";;
testing)
    echo "log out when you're done";;

*) 
    echo "you're not belong here";;
esac


for命令

for var in list
do
    commands
done
最基本的用法是遍歷自身定義的一系列值
for test in Alabma Alaska Arkansas California Colorado
do
    echo The next state is $test
done
在最後一次迭代之後,$test變量仍然有效

shell會將列表值中的單引號作為單獨數據,若僅想使用單引號'在列表內,可以使用雙引號"來定義含有單引號的值 for循環假設定義各個值之間是以 空格 分隔的

從變量中讀取值列表

把變量帶入in後面就好

從命令中讀取值列表

使用命令輸出,並在for迴圈內使用

file="state.txt"

for state in $(cat $file)
do 
    echo "Visit beautiful $state"
done

遍歷目錄

for file in /home/rich/test/* /home/rich/other_test/*
do
    if[ -d "$file" ]
    then
        echo "$file is a directory"
    elif [ -f "$file" ]
    then
        echo "$file is a file"
    fi
done

在linux中,目錄名和文件名包含空格是合法的,要在shell script裡實現,應將 $file 變量加入雙引號內

C語言風格的for loop

基本格式如下

for (( variable assignment ; condition ; iteration process ))

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

Ex

for (( i = 0 ; i <= 10 ; i++ ))
do
    echo "The next number is $1"
done
for 命令也允許可以使用多個變量

for (( a = 1, b = 10; a <= 10 ; a++, b-- ))

更改分隔符號

IFS(Internal field separator)環境變量定義了bash shell用作字段分隔符的一系列字元,默認情況下,bash shell會將下列文字作為分隔符號

  • 空格
  • 制表符號
  • 換行符號

若要更改IFS環境變量,使bash shell能識別換行符號

IFS=$'\n'
將該語句加入腳本,bash shell便會忽略數據中的空格與制表符
file="state.txt"

IFS=$'\n'
for state in $(cat $file)
do
    echo "Visit beautiful $state"
done
如果要遍歷文件中以冒號分隔的值,只需將IFS設為冒號即可
IFS=:
如果要指定多個IFS
IFS=$'\n:;"'

while 命令

格式如下

while test command
do
    other commands
done
例子
var1=10
while [ $var -gt 0 ]
do
    echo $var1
    var1=$[ $var - 1 ]
done

使用多個測試命令

在有多個命令的while語句中,每次迭代時的所有測試命令都會被執行,包括最後一個測試失敗末次迭代

while echo $var1
        [ $var -ge 0 ]
do
    echo "This is inside the loop"
    var1=$[ $var1 -1 ]
done

until命令

while相反,until會持續執行直到測試命令的退出狀態碼不為0

until test commands
do
    other command
done

break

break可以退出當前正在執行的循環 break命令接受單個命令參數n,指定退出的循環

continue

continue 提前結束某次循環

處理循環的輸出

for file in /home/rich/*
do
    if [ -d "$file" ]
        echo "$file is a directory"
    elif [ -f "$file" ]
        echo "$file is a file"
    fi
done > output.txt
這會將結果輸出到output.txt

sed 與 awk

處理文件的工具

sed

sed被稱為 流編輯器 (stream editor),可以執行以下動作

  • 從輸入中讀取一行數據
  • 根據提供的編輯器命令匹配參數
  • 按照命令修改數據流中的數據
  • 將新數據輸出到STDOUT

命令格式如下

sed options script file
* options * -e command : 處理輸入時,加入額外的sed命令 * -f file:處理輸入時,將file中指定的命令添加到已有的命令中 * -n:不產生命令輸出,使用p (print)命令完成輸出

在命令中定義編輯器命令

$ echo "This is a test" | sed 's/test/big test/'
This is a big test

s替換命令用斜線間指定的第二個字符串替換第一個字符串

sed命令並不會修改文本文件的數據

gawk

在gawk中,可以實現以下操作

  • 定義變量來保存數據
  • 使用算術與字符串來處理數據
  • 使用if-then與loop等邏輯處理數據
  • 提取文件的數據並重新排列組合

命令格式如下

gawk options program file

  • options
    • -F fs:指定行中劃分數據分段的分隔符
    • -f file:從指定文件中讀取gawk腳本代碼
    • -v var=value:定義gawk腳本中的變量及默認值
    • -L [keyword]:指定gawk的兼容模式

需要把gawk命令腳本放到單引號內

$ gawk '{print "Hello World!"}'
This is a test
Hello World!
This is another test
Hello World!
要終止這個gawk程序,需要使用Ctrl+D生成EOF字符