初學者編寫bash腳本教程

初學者編寫bash腳本教程html

bash shell script 定義

bash

bash是命令語言解釋器。普遍用於各類gun/unix系統上的默認命令解釋器。全程叫作「Bourne-Again SHell」mysql

shell

shell是一個宏處理器,容許執行交互式或非交互式的命令。linux

scripting

腳本容許自動執行,不然會一個接一個命令交互執行。程序員

什麼是shell

shell容許你經過命令與計算機交互,從而檢索或存儲數據、處理信息和其它各類簡單甚至很是複雜的任務。sql

因此:你天天都會執行date cal pwd lsdocker

什麼是scripting

script就是把上邊的4個命令放在一個文本里,而後一塊兒執行。shell

bash是什麼

bash是許多GNU/Linux系統上的默認解釋器編程

查看默認解釋器執行命令:echo $SHELLubuntu

[root@localhost ~]# echo $SHELL
/bin/bash

#! shebang,定義腳本的shell解釋器,寫在腳本的第一行如:數組

#!/bin/bash

經過which bash查找bash可執行二進制文件的完整路徑。

文件名和權限

要執行腳本,就要給腳本加執行的權限:chmod +x filename

經過file 命令查看文件的類型 file filename

[root@localhost ~]# file a.sh 
a.sh: Bourne-Again shell script, ASCII text executable
[root@localhost ~]# file epel-7.repo 
epel-7.repo: ASCII text

腳本執行

一、指定解釋器執行腳本

[root@localhost ~]# /bin/bash date.sh 
Sat Jul 14 09:01:39 UTC 2018

二、腳本文件頭定義shebang,文件加執行權限

[root@localhost ~]# cat date.sh 
#!/bin/bash

date
[root@localhost ~]# chmod +x date.sh 
[root@localhost ~]# ./date.sh 
Sat Jul 14 09:02:41 UTC 2018

相對路徑和絕對路徑

絕對路徑:

在linux中絕對路徑就是從根/開始,如:/usr/local/httpd

相對路徑:

就是路徑開頭沒有根/,如:cd abc/test/

路徑切換

cd 不加任何參數,切換到當前用戶home目錄。

cd ~ 都是切換到當前用戶home目錄。

cd 加路徑 切換到目標路徑,如:cd /usr/local

hello world 腳本

[root@localhost ~]# echo '#!/bin/bash' > hello.sh      
[root@localhost ~]# echo 'echo "hello world"' >> hello.sh 
[root@localhost ~]# cat hello.sh                     
#!/bin/bash
echo "hello world"
[root@localhost ~]# chmod +x hello.sh 
[root@localhost ~]# ./hello.sh 
hello world

簡單備份bash shell腳本

vi backup.sh    #建立腳本
# !/bin/bash
tar czf /tmp/root.tar /root/abc

chmod +x backup.sh  #加執行權限

./backup.sh #執行腳本

[root@localhost]# ls /tmp/root.tar  #驗證執行結果
/tmp/root.tar

經常使用變量

給變量賦值的不一樣方式

雙引號"" 容許用過$符號引用其餘變量值

單引號'' 禁止引用其餘變量值,$視爲普通字符

反撇號`` 將命令執行的記過輸出給變量;如:

sed -i s/"B"/"b"/g `grep -rl "B" --exclude="*.sql" ceshi/*`  將grep獲得的文件傳輸給sed使用

用戶自定義變量

設置變量的做用範圍

格式:

export 變量名

export 變量名=變量值

清除變量名:

unset 變量名

自定義變量
[root@ceshi ~]# export a            #自定義變量a
[root@ceshi ~]# export b=222        #自定義變量b
[root@ceshi ~]# a=111               #給變量a賦值

[root@ceshi ~]# echo $a             #打印變量a的值
111
[root@ceshi ~]# echo $b             #打印變量b的值
222

清除變量:
[root@ceshi ~]# unset a             #清除變量a
[root@ceshi ~]# unset b             #清除變量b
[root@ceshi ~]# echo $a             #輸出爲空

[root@ceshi ~]# echo $b

環境變量

環境變量配置文件

  • 全局配置文件:/etc/profile

  • 用戶配置文件:~/.bash_profile

查看環境變量:

set 命令能夠查看全部的shell變量,其中包括環境變量

常見的環境變量

  • $USER 查看當前用戶
  • $logname 登陸相關信息
  • $UID 當前用戶的UID,root用戶爲0
  • $SHELL 打印當前用戶的shell編輯器
  • $HOME 打印當前用戶的主目錄
  • $pwd 打印當前所在目錄
  • $PATH 用戶輸入的命令在哪裏目錄中查找
  • $PS1
  • $PS2
  • $RANDOM 顯示一個隨機數

位置變量

表示爲:$n (n爲1-9之間的數字)

#./test.sh one two three four five six
  • $0 表示腳本文件名自己

  • $1 表示one

  • $2 表示two

    依次類推

    $n 這個程式的第n個參數值,n=1..9

預約義變量

  1. $# 命令行中位置參數的個數
  2. $* 全部位置參數的內容
  3. $? 上一條命令執行後返回的狀態,爲0表示成功,爲非0表示執行異常或出錯
  4. $$ 當前所在進程的進程號
  5. $! 上一個指令PID 後臺運行的最後一個進程號
  6. $0 當前執行的進程/程序名
  7. $- 顯示shell使用的當前選項,與set命令功能相同
  8. $@ 跟$*相似,可是能夠看成數組用

變量

變量是編程的本質。變量容許程序員在整個腳本中存儲、修改和重用數據。

建立一個新腳本welcome.sh:

#!/bin/bash

greeting="Welcome"
user=$(whoami)
day=$(date +%A)

echo "$greeting back $user! Today is $day."
echo "Your bash shell version is:$BASH_VERSION."

解釋腳本:

第一部分:

shebang 指定shell解釋器

greeting:定義一個變量,並賦值一個字符串值。

user:定義一個變量,賦值是經過一個命令替換技術來完成的。經過運行whoami獲取當前的用戶名,而後賦值給user。

day:定義一個變量,賦值同上;+%A表明只顯示周幾。

第二部分:

使用echo向終端打印信息,$greeting用來調用變量對應的值Welcome;$user一樣用來調用變量對應的用戶名;date也同樣。

注意:不要使用大寫命名私有變量,由於大寫變量名都是內置變量。若是覆蓋內置變量,可能致使功能失調。

終端使用變量

[root@localhost ~]# a=5 #定義變量
[root@localhost ~]# b=6
[root@localhost ~]# echo $a #調用變量
5
[root@localhost ~]# echo $b
6
[root@localhost ~]# echo $[$a+$b] #變量算數
11

利用變量更新備份腳本,生成更有意義的文件名:

vi backup.sh

#!/bin/bash
# this bash script is backup a general user's home directory to /tmp/

user=$(whoami)
input=/home/$user
output=/tmp/${user}_home$(date +%Y-%m-%d_%H%M%S).tar.gz

tar czf $output $input
echo "Backup of $input completed!! Details about the output backup file:"
ls -l $output

腳本第五行:${parameter}叫參數展開,將花括號中的內容挨個和外面的內容結合。

[abc@localhost ~]$ ./backup.sh 
tar: Removing leading `/' from member names
Backup of /home/abc completed!! Details about the output backup file:
-rw-rw-r-- 1 abc abc 741 Jul 14 13:59 /tmp/abc_home2018-07-14_135933.tar.gz

輸入、輸出、錯誤重定向

名詞

一般在GNU/Linux命令行上執行的命令要麼產生輸出,要麼要求輸入,要麼拋出錯誤消息。這是shell腳本的基本概念,也是使用GNU/Linux命令行的基本概念。

每次執行命令時,可能會發生三種可能的結果。第一個場景是命令將產生一個預期的輸出,其次,該命令將生成一個錯誤,最後,您的命令可能根本不會產生任何輸出。

標準輸出以下:

終端輸出

[abc@localhost ~]$ ls backup.sh 
backup.sh

重定向輸出

重定向標識符:「>」

一、">" 輸出重定向

重定向會將 標準輸出 重定向到指定目標。

[abc@localhost ~]$ touch foobar 
[abc@localhost ~]$ ls foobar barfoo
ls: cannot access barfoo: No such file or directory
foobar
[abc@localhost ~]$ ls foobar barfoo > stdout.txt
ls: cannot access barfoo: No such file or directory
[abc@localhost ~]$ cat stdout.txt 
foobar

二、"2>" 錯誤輸出重定向

重定向將 標準錯誤 重定向到指定目標。

[abc@localhost ~]$ ls foobar barfoo 2> stderr.txt
foobar
[abc@localhost ~]$ cat stderr.txt 
ls: cannot access barfoo: No such file or directory

三、"&>" 輸出和錯誤 重定向 輸出

重定向將 標準輸出和標準錯誤 一塊重定向到指定目標。

[abc@localhost ~]$ ls foobar barfoo &> stdoutandstderr.txt
[abc@localhost ~]$ cat stdoutandstderr.txt 
ls: cannot access barfoo: No such file or directory
foobar

四、「>>」 追加劇定向輸出

追加劇定向輸出,不覆蓋文件,直接追加到文件結尾。

標準錯誤:

一、終端輸出

[abc@localhost ~]$ ls test.sh
ls: cannot access test.sh: No such file or directory

二、"2>" 錯誤輸出 重定向

重定向將 標準錯誤 重定向到指定目標。

[abc@localhost ~]$ ls foobar barfoo 2> stderr.txt
foobar
[abc@localhost ~]$ cat stderr.txt 
ls: cannot access barfoo: No such file or directory

標準輸入

終端輸入

一般終端輸入來自鍵盤,因此輸入的任何內容都稱爲stdin(標準輸入)。

文件輸入

接受來自文件的命令輸入。使用「<」符號接受文件輸入。

咱們先用cat 命令,經過標準輸入而後,重定向到file.txt:

[abc@localhost ~]$ cat > file.txt
hello world.
welcome backup.
[abc@localhost ~]$ cat file.txt 
hello world.
welcome backup.

咱們再用cat命令,經過< 符號輸入file.txt的內容:

[abc@localhost ~]$ cat < file.txt 
hello world.
welcome backup.

重定向輸入

一、「<」 輸入重定向

[root@localhost log]# cat >aaa.txt<<EOF
> hello world
> EOF
[root@localhost log]# cat aaa.txt 
hello world

二、「<<」 追加輸入重定向

[root@localhost log]# cat >>aaa.txt<<EOF
> 123456789
> HELLO WORLD
> EOF
[root@localhost log]# cat aaa.txt 
hello world
123456789
HELLO WORLD

read輸入

從鍵盤或文件中獲取標準輸入:read命令

語法:read 變量名

[abc@localhost ~]$ read a
abc
[abc@localhost ~]$ echo $a
abc
[abc@localhost ~]$ echo "the word you entered is: $a"   
the word you entered is: abc
[abc@localhost ~]$ read a1 a2 #同時輸入兩個字段
aaa bbb
[abc@localhost ~]$ echo $a1
aaa
[abc@localhost ~]$ echo $a2
bbb

詳細見:https://blog.51cto.com/506554897/2114407

backup.sh優化

以前的backup腳本執行時有個錯誤:

tar: Removing leading "/" from member names

消息告訴咱們,絕對路徑已被刪除,從而提取了壓縮文件,而不是覆蓋任何現有文件。並無形成任何影響。

所以我能夠利用重定向消除這種沒必要要的信息。經過重定向stderr到/dev/null,/dev/null做爲數據接收器,它丟棄任何重定向到它的數據,能夠用man null查看詳細。

咱們將腳本修改以下:

[abc@localhost ~]$ vi backup.sh 
#!/bin/bash
# this bash script is backup a general user's home directory to /tmp/

user=$(whoami)
input=/home/$user
output=/tmp/${user}_home$(date +%Y-%m-%d_%H%M%S).tar.gz

tar czf $output $input 2> /dev/null
echo "Backup of $input completed!! Details about the output backup file:"
ls -l $output
[abc@localhost ~]$ ./backup.sh 
Backup of /home/abc completed!! Details about the output backup file:
-rw-rw-r-- 1 abc abc 893 Jul 14 14:59 /tmp/abc_home2018-07-14_145919.tar.gz

執行後沒有了報錯信息。

函數

##名詞

函數容許程序員組織和重用代碼,從而提升了整個腳本的效率、執行速度和可讀性。

當注意到腳本包含兩行相同的代碼時,能夠考慮使用一個函數。

函數是將不一樣命令的組號組合成當個命令的方法。若是所需的輸出或計算由多個命令組成,而且在整個腳本執行過程當中須要屢次,這可能很是有用。函數是使用函數關鍵字定義,後面是用花括號括起來的函數體。如:user_details {}

定義函數方法:

function 函數名{
  函數體(命令)
}
函數名() {
  函數體(命令)
}
函數名() 
{
  函數體(命令)
}

看個例子:

[abc@localhost ~]$ vi function.sh       
#!/bin/bash

function user_details {
    echo "User Name: $(whoami)"
    echo "User Home: $HOME"
}

user_details

詳細描述:

function是函數關鍵字,user_details是函數名。

花括號裏邊的是函數體,包括兩個echo命令,在兩個echo以前有縮進,縮進爲1個tab,4個空格。縮進能夠擇本身的縮進方式。

最後的user_details 是在調用函數。

注意:函數定義必須在函數調用以前進行,不然腳本將返回stderr:command not found錯誤。

優化backup.sh

#!/bin/bash

# this bash script is backup a general user's home directory to /tmp/

user=$(whoami)
input=/home/$user
output=/tmp/${user}_home$(date +%Y-%m-%d_%H%M%S).tar.gz

function total_files {
    find $1 -type f | wc -l 
}

function total_directory {
    find $1 -type d | wc -l 
}

tar czf $output $input 2> /dev/null

echo -n "files to be included:"
total_files $input

echo -n "directory to be included:"
total_directory $input

echo "Backup of $input completed!!" 

echo "Details about the output backup file:" 
ls -l $output

代碼片斷:

定義tottal_files函數,功能利用function find() { [native code] }和wc命令,來統計 提供給函數調用的目錄的文件數量。

定義total_diretory函數,功能利用function find() { [native code] }和wc命令,來統計 提供給函數調用的目錄的目錄數量。

執行腳本顯示以下:

 
 

數值和字符串比較

數字和字符串的基本比較運算符

描述 數值比較 字符串比較
少於 less than -lt <
大於 greater than -gt >
等於 equal -eq =
不等於 not equal -ne !=
小於或等於 -le N/A
大於或等於 -ge N/A
字符串不是空 N/A -n
字符串是空 N/A -z

N/A:表明不可用/不適用

-n $1:字符串$1不是空的

-z $1:字符串$1 爲空

當兩個數值比較後,能夠用echo $?來檢查返回值,來查看結果是True 返回0;結果是False返回爲1。

數值比較

[abc@localhost ~]$ a=1
[abc@localhost ~]$ b=2
[abc@localhost ~]$ [ $a -lt $b ]
[abc@localhost ~]$ echo $?  #正確返回0
0
[abc@localhost ~]$ [ $a -gt $b ] 
[abc@localhost ~]$ echo $?  #不正確返回1
1
[abc@localhost ~]$ [ $a -eq $b ]  
[abc@localhost ~]$ echo $?      
1

字符串比較

[abc@localhost ~]$ [ "apple" = "banana" ]
[abc@localhost ~]$ echo $?
1
[abc@localhost ~]$ str1="apple"
[abc@localhost ~]$ str3="apple"
[abc@localhost ~]$ str2="banana"
[abc@localhost ~]$ [ $str1 = $str2 ]
[abc@localhost ~]$ echo $?
1
[abc@localhost ~]$ [ $str1 = $str3 ]
[abc@localhost ~]$ echo $?          
0

if /else/fi條件語句

名詞

條件容許程序員根據某些條件或事件在shell腳本中實現決策。

咱們所指的條件語句是:if、then、else。

案例1-比較大小

舉個例子:if num1大於num2,then 就打印num1大於2,else 若是不大於,就打印其餘信息。

[abc@localhost ~]$ vi num.sh
#!/bin/bash
num1=100
num2=200

if [ $num1 -gt $num2 ];then
    echo "$num1 greater than num2"
else
    echo "$num1 less than or equal $num2"
fi

注意fi 是關閉if條件塊的關鍵詞;注意中括號兩邊的間距,沒有空間,就不能執行。

[abc@localhost ~]$ chmod +x num.sh 
[abc@localhost ~]$ ./num.sh 
100 less than or equal 200

案例2-判斷目錄是否存在

查看當前目錄下是否有mysql目錄

[abc@localhost ~]$ vi mysql.sh 
#!/bin/bash

directory="./mysql"

if [ -d $directory ]; then
    echo "$directory is exist!!!"
else
    echo "$directory is not exist!"
fi

執行結果:

[abc@localhost ~]$ sh mysql.sh 
./mysql is not exist!
[abc@localhost ~]$ mkdir mysql
[abc@localhost ~]$ sh mysql.sh 
./mysql is exist!!!

案例3-嵌套if/else

[abc@localhost ~]$ cat nested.sh 
#!/bin/bash

# 這是一個嵌套if else腳本,while循環讓用戶循環選擇字段。

# 定義一個標誌
choise=0

# 定義一個函數,打印用戶可選擇的序號 字段
function p1() {
    echo "1. BeiJing"
    echo "2. ShangHai"
    echo "3. ChangSha"
    echo "請選擇你喜歡城市的序號[1-3]"
}

# 當choise爲0的時候就循環
while [ $choise -eq 0 ]; do
    p1
    read choise
    if [ $choise -eq 1 ]; then
        echo "你選擇的是:Beijing"
    else
        if [ $choise -eq 2 ]; then
            echo "您的選擇是: ShangHai"
        else
            if [ $choise -eq 3 ]; then
                echo "您的選擇是:ChangSha"
            else
                echo 「請輸入合法的字段」
                choise=0
            fi
        fi
    fi
done

執行結果:

[abc@localhost ~]$ sh nested.sh 
1. BeiJing
2. ShangHai
3. ChangSha
請選擇你喜歡城市的序號[1-3]
1
你選擇的是:Beijing

[abc@localhost ~]$ sh nested.sh 
1. BeiJing
2. ShangHai
3. ChangSha
請選擇你喜歡城市的序號[1-3]
4
「請輸入合法的字段」
1. BeiJing
2. ShangHai
3. ChangSha
請選擇你喜歡城市的序號[1-3]
3
您的選擇是:ChangSha

[abc@localhost ~]$ sh nested.sh 
1. BeiJing
2. ShangHai
3. ChangSha
請選擇你喜歡城市的序號[1-3]
2
您的選擇是: ShangHai

案例4-if/elif.../else/fi

[abc@localhost ~]$ cat elif.sh 
#!/bin/bash

# 經過用戶輸入兩個數值,分別賦值給num一、num2,而後來比較大小
read num1 num2

if [ $num1 -eq $num2 ]; then
    echo "Both values are equal!"
elif [ $num1 -gt $num2 ]; then
    echo "num1 values is greater then num2"
else
    echo "num1 values is less then num2"
fi

執行結果:

[abc@localhost ~]$ sh elif.sh 
11 22
num1 values is less then num2
[abc@localhost ~]$ sh elif.sh 
11 11
Both values are equal!
[abc@localhost ~]$ sh elif.sh 
6 2
num1 values is greater then num2

優化backup.sh

執行完tar備份以後,要比較源數據的文件數量和備份後tar包的文件的數量。

#!/bin/bash

# this bash script is backup a general user's home directory to /tmp/

user=$(whoami)
input=/home/$user
output=/tmp/${user}_home$(date +%Y-%m-%d_%H%M%S).tar.gz

function total_files {
    find $1 -type f | wc -l 
}

function total_directory {
    find $1 -type d | wc -l
}

function total_archived_directory {
    tar -tzf $1 | grep /$ |wc -l
}

function total_archived_files {
    tar -tzf $1 | grep -v /$ | wc -l
}

tar czf $output $input 2> /dev/null

src_files=$( total_files $input )
src_directory=$( total_directory $input )

arch_files=$( total_archived_files $output )
arch_directory=$( total_archived_directory $output )

echo "Files to be include: $src_files"
echo "Directories to be include: $src_directory"
echo "Files archived: $arch_files"
echo "Directories archived: $arch_directory"

if [ $src_files -eq $arch_files ];then
    echo "Backup of $input completed!!!"
    echo "Details about the output backup file:"
    ls -l $output
else
    echo "Backup of $input Failed!!!"
fi

bash文件測試

bash文件測試列表

指令 描述
-b filename 塊特殊文件
-c filename 特殊字符文件
-d directory name 檢查目錄是否存在
-e filename 檢查文件是否存在
-f filename 檢查是否存在常規文件,而不是目錄
-G filename 檢查文件是否存在,是否有效組ID
-g filename 若是文件存在並設置了組id,則爲true。
-k filename Sticky bit
-L filename 符號連接 Symbolic link
-O filename 若是文件存在並有效用戶id,則爲true。
-r filename 檢查文件是否可讀
-S filename 檢查文件是否爲socket
-s filename 檢查文件大小是否爲非零
-u filename Check if file set-ser-id bit is set
-w filename 檢查文件是否可寫
-x filename 檢查文件是否可執行

例子

[abc@localhost ~]$ vi file-test.sh
#!/bin/bash

# this is a test file exist ? yes/no

file="./testtest.txt"
if [ -e $file ]; then
    echo "File exists!"
else
    echo "File does not exists"
fi

腳本執行結果:

[abc@localhost ~]$ /bin/bash file-test.sh 
File does not exists
[abc@localhost ~]$ touch testtest.txt
[abc@localhost ~]$ /bin/bash file-test.sh 
File exists!

位置參數

名詞

到目前爲止,咱們的備份腳本看起來很棒。咱們能夠計算結果壓縮備份文件中包含的文件和目錄的數量。此外,咱們的腳本還促進了一個健全檢查,以確認全部文件已正確備份。缺點是咱們老是被迫備份當前用戶的目錄。若是腳本足夠靈活,容許系統管理員僅經過將腳本指向主目錄就能夠備份所選系統用戶的主目錄,那就太好了。

當使用bash位置參數時,這是一個至關容易的任務。位置參數經過命令行參數分配,並可在腳本中訪問,如$1, $2...$N變量。在腳本執行期間,在程序名稱以後提供的任何附加項都被視爲參數,並在腳本執行期間可用。

$、$# 、$*

考慮如下示例:

[abc@localhost ~]$which bash > position.sh 
[abc@localhost ~]$ vi position.sh 
#!/usr/bin/bash

echo $1 $2 $4
echo $#
echo $*
[abc@localhost ~]$ /bin/bash position.sh 1 2 3 4
1 2 4
4
1 2 3 4
[abc@localhost ~]$ /bin/bash position.sh one two three four
one two four
4
one two three four

腳本詳細:

#!/usr/bin/bash
echo $1 $2 $4
echo $#
echo $*

第二行表明打印:第一個、第二個、第四個 位置參數

第三行表明打印:$#提供參數的總和

第四行表明打印:$*提供的全部參數

優化backup.sh

使用位置參數知識,讓咱們如今改進咱們的backup.sh腳原本接受命令行中的參數。實現讓用戶決定哪一個目錄將被備份。若是用戶在腳本執行期間沒有提交任何參數,默認狀況下,該腳本將備份當前用戶的主目錄。新腳本以下:

#!/bin/bash 
# this bash script is used to backup a user's home directory 

if [ -z $1 ];then
    user=$(whoami)
else
    if [ ! -d "/home/$1" ]; then
        echo "Requested $1 user home directory doesn't exist!"
        exit 1
    fi
    user=$1
fi

# define default backup directory and tar.gz files
input=/home/$user
output=/tmp/${user}_home_$(date +%Y-%m-%d_%H%M%S).tar.gz

# count the number of source files
function total_files {
    find $1 -type f | wc -l 
}
# count the number of source directory
function total_directory {
    find $1 -type f | wc -l
}

# count the number of archived files
total_archived_files() {
    tar -tzf $1 | grep /$ | wc -l
}
# count the number of archived directory
total_archived_directory() {
    tar -tzf $1 | grep -v /$ | wc -l
}

tar -czf $output $input 2> /dev/null

src_files_num=$( total_files $input )
src_directory_num=$( total_directory $input)

arch_files_num=$( total_archived_files $output)
arch_directory_num=$( total_archived_directory $output)

echo "files to be included: $src_files_num"
echo "directory to be included: $src_directory_num"

echo "files to be included of archived: $arch_files_num"
echo "directory to be included of archived: $arch_directory_num"

if [ $src_files_num -eq $arch_files_num ]; then
    echo "Backup of $input complete!!"
    echo "Deatils about the output backup file:"
    ls -l $output
else
    echo "Backup of $input Failed!!!"
fi

腳本執行結果:

#不加參數執行,默認備份當前用戶主目錄
[abc@localhost ~]$ ./backup.sh 
files to be included: 21
directory to be included: 21
files to be included of archived: 1
directory to be included of archived: 21
Backup of /home/abc Failed!!!
#加參數指定用戶abc,備份abc用戶的主目錄
[abc@localhost ~]$ ./backup.sh abc
files to be included: 21
directory to be included: 21
files to be included of archived: 1
directory to be included of archived: 21
Backup of /home/abc Failed!!!
#加參數指定用戶aaa,aaa用戶不存在,報錯了。
[abc@localhost ~]$ ./backup.sh aaa
Requested aaa user home directory doesn't exist!

bash 循環

loop名詞

到目前爲止,本分腳本功能和可用性已經大大提升,經過位置參數咱們能夠指定備份 某個用戶的主目錄,咱們能夠輕鬆的備份任何用戶目錄。

當咱們須要天天備份多個用戶目錄時,就會變得重複、乏味、費時。

這個時候咱們能夠用循環構造來迭代任何給定數量的任務。

有三種基本循環類型。

for循環

for循環用於迭代列表中任意數量的提供項的任何給定代碼。讓咱們從一個簡單的for循環示例開始:

[abc@localhost ~]$ for i in 1 2 3; do echo $i; done
1
2
3

上邊的for循環使用了echo 命令打印因此項目。使用分號容許在當行執行for循環。將上面的for循環變成bash腳本,以下:

[abc@localhost ~]$ vi loop.sh        
#!/bin/bash

for i in 1 2 3; do
    echo $i
done
~

for循環由四個shell保留字段組成:for、in、do、done。

所以,上述代碼能夠理解爲:for 循環in在列表中的每一項,而後將每一項臨時賦值給變量i,而後doecho $i 打印到stdout,直到列表循環完才結束done

for循環例子

計算文本里邊每行的字符數量

[abc@localhost ~]$ cat items.txt 
abc
abcd
abcdef
a
[abc@localhost ~]$ for i in $( cat items.txt ); do echo -n $i | wc -c; done
3
4
6
1

while循環

while循環在給定的條件下工做。它將繼續執行封閉的代碼。當條件爲true的時候,do 執行代碼,當條件變爲false就終止done

[abc@localhost ~]$ vi while.sh
#!/bin/bash

counter=0
while [ $counter -lt 3 ]; do
    let counter+=1
    echo $counter
done

代碼詳解:

定義變量:counter

while定義條件:當變量counter小於3的時候,條件爲true。在每次循環迭代期間,使用let將counter自加1,而後打印變量counter。

done:當條件變成false 結束循環

腳本執行結果:

[abc@localhost ~]$ sh while.sh 
1
2
3

until循環

until循環和while循環徹底相反的操做。利用until、do、done,循環直到條件從false變成true就結束。

[abc@localhost ~]$ vi until.sh
#!/bin/bash

counter=6
until [ $counter -lt 3 ]; do
    let counter-=1
    echo $counter
done

代碼詳解:

定義變量:counter

until定義條件:當變量counter不小於3的時候,條件爲false。在每次循環迭代期間,使用let將counter自加1,而後打印變量counter。

done:當條件變成true 結束循環

腳本執行結果:

[abc@localhost ~]$ sh until.sh 
5
4
3
2

優化backup.sh

以前的備份腳本每次執行備份一個用戶的目錄。咱們能夠經過位置參數給腳本提供多個用戶,經過for循環去執行備份命令。以下:

#!/bin/bash

# this bash script is used to backup one or more user's home directory

function backup {
    if [ ! -d "/home/$1" ]; then
        echo "resquested $1 user home directory does't exist."
        exit 1
    fi
    user=$1

    input=/home/$user
    output=/tmp/${user}_home_$(date +%Y-%m-%d_%H%M%S).tar.gz

    function total_files {
        find $1 -type f | wc -l
    }
    function total_directories {
        find $1 -type d | wc -l
    }

    function total_arch_files {
        tar -xvf $1 | grep -v /$ | wc -l    
    }
    function total_arch_directories {
        tar -xvf $1 | grep /$ | wc -l    
    }

    tar -czf $output $input 2> /dev/null

    # count the number of src_files and src_directory
    src_files=$( total_files $input )
    src_directories=$( total_directories $input )

    arch_files=$( total_arch_files $output )
    arch_directories=$( total_arch_directories $output )

    echo "######## $user ########"
    echo "Src_files to be included: $src_files"
    echo "Src_directories to be included: $src_directories"
    echo "Arch_files to be included: $arch_files"
    echo "Arch_directories to be included: $arch_directories"

    if [ $src_files -eq $arch_files ]; then
        echo "--------------------------------------"
        echo "Backup user:<$user> home completed!!!"
        echo "Details about the output backup files:"
        ls -l $output
    else
        echo "Backup user:<$user> home Failed!!!"
    fi
}

for directory in $*; do
    backup $directory
done;

該腳本最好用root用戶去執行,由於普通用戶備份別的用戶home目錄會存在權限問題。但該腳本並不能備份root的home目錄。腳本執行結果:

[root@localhost ~]# sh loop_backup.sh aaa bbb ccc
######## aaa ########
Src_files to be included: 4
Src_directories to be included: 1
Arch_files to be included: 4
Arch_directories to be included: 1
--------------------------------------
Backup user:<aaa> home completed!!!
Details about the output backup files:
-rw-r--r-- 1 root root 519 Jul 17 04:11 /tmp/aaa_home_2018-07-17_041134.tar.gz
######## bbb ########
Src_files to be included: 5
Src_directories to be included: 2
Arch_files to be included: 5
Arch_directories to be included: 2
--------------------------------------
Backup user:<bbb> home completed!!!
Details about the output backup files:
-rw-r--r-- 1 root root 573 Jul 17 04:11 /tmp/bbb_home_2018-07-17_041134.tar.gz
######## ccc ########
Src_files to be included: 6
Src_directories to be included: 3
Arch_files to be included: 6
Arch_directories to be included: 3
--------------------------------------
Backup user:<ccc> home completed!!!
Details about the output backup files:
-rw-r--r-- 1 root root 606 Jul 17 04:11 /tmp/ccc_home_2018-07-17_041134.tar.gz

select語句

select表達式是bash的一種擴展應用,擅長於交互式場合。

語法:

select var in ... ; do

    break;

done

例子:

[abc@localhost ~]$ vi select.sh 
#!/bin/bash

PS3="請選擇你最喜歡城市的序號:"

select word in "北京" "上海" "杭州" "長沙"; do
    echo "您選擇的是:$word"
    break
done

執行結果:

[abc@localhost ~]$ sh select.sh 
1) 北京
2) 上海
3) 杭州
4) 長沙
請選擇你最喜歡城市的序號:1
您選擇的是:北京

case語句

case語句爲多選擇語句。能夠用case語句來匹配一個值與一個模式,若是匹配成功,執行相匹配的命令。

case語法:

case 值 in
模式1)
    command1
    command2
    ...
    ;;  # 兩個分號表明結束
模式2)
    command1
    command2
    ...
    ;;
esac

如上:case的值後面必須是in,每一個模式必須以 右括號結束,取值能夠是變量或常數。匹配某一模式後,就會執行模式下的全部命令 直到 ;; 。

取值一旦匹配了某個模式,就不會再去匹配其餘模式。若是沒有匹配到,就會用 * 捕獲該值,再執行後面的命令。

case語句最後要有一個結束標記:esac 。

例子:

[abc@localhost ~]$ cat case.sh 
#!/bin/bash

echo "請輸入你喜歡的城市:"
echo "1) 北京"
echo "2) 上海"
echo "3) 杭州"
echo "4) 長沙"

read num

case $num in
1) 
    echo "你選擇的是北京"
    echo 1
    ;;
2)
    echo "你選擇的是上海"
    echo 2
    ;;
3)
    echo 「你選擇的是杭州」
    ;;
4)
    echo "你選擇的是長沙"
    ;;
*)
    echo "輸入錯誤!請選擇[1-4]城市對應的序號!"
    ;;
esac

執行結果:

[abc@localhost ~]$ sh case.sh 
請輸入你喜歡的城市:
1) 北京
2) 上海
3) 杭州
4) 長沙
1
你選擇的是北京
1
[abc@localhost ~]$ sh case.sh 
請輸入你喜歡的城市:
1) 北京
2) 上海
3) 杭州
4) 長沙
3
「你選擇的是杭州」
[abc@localhost ~]$ sh case.sh 
請輸入你喜歡的城市:
1) 北京
2) 上海
3) 杭州
4) 長沙
5
輸入錯誤!請選擇[1-4]城市對應的序號!

算術運算

整數運算

let運算命令

[root@ceshi ~]# vi let.sh
#!/bin/bash
num1=2
num2=3
let result=num1+num2
echo $result

運行:
[root@ceshi ~]# /bin/bash let.sh 
5
  1. 自加操做 let num1++

  2. 自減操做 let num1--

  3. 簡寫形式:let no+=10let ; let no-=20

    等同於:let no=no+10; let no=no-20

操做符[]運算方法

[root@ceshi ~]# vi fangkuohao.sh
#!/bin/bash
num1=2
num2=3
result=$[$num1+num2]
echo $result

運行:
[root@ceshi ~]# /bin/bash fangkuohao.sh 
5

注:使用方法和let類似,在[]中可使用$前綴

(())運算方法

[root@ceshi ~]# vi xiaokuohao.sh
#!/bin/bash
n1=2
n2=3
result=$((n1+n2))
echo $result

expr運算方法

[root@ceshi ~]# expr 2 + 3         
5
[root@ceshi ~]# num1=5
[root@ceshi ~]# r=$(expr $num1 + 5)
[root@ceshi ~]# echo $num1
5
[root@ceshi ~]# echo $r
10

expr的經常使用運算符

  • +
  • -
  • *
  • /
  • % 取模運算,也叫取餘

精密計算

高級運算工具:bc

它能夠執行浮點運算和一些高級函數

[root@ceshi ~]# echo "1.25*3" | bc
3.75

設定小數精度(也就是小數點顯示幾位)

scale=2 表明小數點顯示2位

[root@ceshi ~]# echo "scale=2;7/3" | bc  
2.33

進制轉換

十進制轉二進制:
[root@ceshi ~]# a=192
[root@ceshi ~]# echo "obase=2;$a" |bc
11000000

二進制轉十進制:
[root@ceshi ~]# b=11000000                   
[root@ceshi ~]# echo "obase=10;ibase=2;$b"|bc
192

計算平方和平方根

求2的三次方:
[root@ceshi ~]# echo "2^3"|bc  
8

求100的平方根
[root@ceshi ~]# echo "sqrt(100)"|bc
10

trap 命令

用於指定在接收到信號後將要採起的動做,常見的用途是在腳本程序被中斷時完成清理工做、忽略ctrl+c。

當腳本在執行的時候,在終端駛入ctrl+c,trap會接收到消息,並執行相關trap命令。以下:

[abc@localhost ~]$ cat traptrap.sh 
#!/bin/bash

# difine bash trap command
trap bashtrap INT

# clear screen command
clear;

# define trap function:bashtrap, bashstrap is executed when CTRL-C is pressed;
bashtrap()
{
    echo "bash trap detected "CTRL+C" when script is executed. "
}

# for loop from 1/10 to 10/10
for a in `seq 1 10`; do
    echo "$a/10 to exit"
    sleep 1;
done

echo "exit bash trap example!"

當腳本在執行的時候,在終端輸入ctrl+c,trap到以後會執行bashtrap函數。執行結果以下:

[abc@localhost ~]$ sh traptrap.sh 
1/10 to exit
2/10 to exit
^Cbash trap detected CTRL+C when script is executed. 
3/10 to exit
^Cbash trap detected CTRL+C when script is executed. 
4/10 to exit
^Cbash trap detected CTRL+C when script is executed. 
5/10 to exit
6/10 to exit
7/10 to exit
^Cbash trap detected CTRL+C when script is executed. 
8/10 to exit
9/10 to exit
10/10 to exit
exit bash trap example!

數組

詳見:第四章 數組、關聯數組和別名使用

https://blog.51cto.com/506554897/2114414

聲明簡單bash數組

[abc@localhost ~]$ vi array.sh 
#!/bin/bash

# 聲明數組array,並賦值3個元素
array=( "Debian linux" "redhat linux" "ubuntu linux")

# 得到數組array裏邊有多少個元素
elements=${#array[@]}

# 經過for循環數組裏邊的元素的索引,並經過數組索引打印每一個元素
for (( i=0;i<$elements;i++)); do
    echo ${array[${i}]}
done

執行結果:

[abc@localhost ~]$ sh array.sh 
Debian linux
redhat linux
ubuntu linux

將文件讀入bash數組

[abc@localhost ~]$ cat read.sh 
#!/bin/bash

# 這是一個讀取文件每行內容到數組的腳本。

# 聲明一個變量array
declare -a array

# 用stdin連接文件記錄器10
exec 10<&0

# 將stdin替換爲做爲第一個參數提供的文件。
exec < $1

# 用while循環將文件每行內容做爲一個元素寫入數組
# count用做數組的下標
let count=0 
while read line; do
    echo "line text is: $line"
    array[$count]=$line
    ((count++))
done

# 打印數組的元素總個數,@和* 這裏是一個意思 都是全部
echo "count the number of array elements: ${#array[@]}"
# 打印數組的全部元素
echo "array content is: ${array[*]}"

# 從filedescriptor 10恢復stdin; filedescriptor:文件記錄器
# 關閉filedescriptor 10
exec 0<&10 10<&-

declare 聲明變量和顯示變量 詳見:第二十八章 聲明和顯示shell變量:declare命令

執行結果:

[abc@localhost ~]$ cat a.txt 
welcome
to
china
[abc@localhost ~]$ sh read.sh a.txt 
line text is: welcome
line text is: to
line text is: china
count the number of array elements: 3
array content is: welcome to china
[abc@localhost ~]$ cat b.txt 
大母雞 小母雞
母雞 母雞
老母雞
[abc@localhost ~]$ sh read.sh b.txt 
line text is: 大母雞 小母雞
line text is: 母雞 母雞
line text is: 老母雞
count the number of array elements: 3
array content is: 大母雞 小母雞 母雞 母雞 老母雞

bash 引用-轉義

轉義元字符

\ 反斜槓就是轉義特殊符號。

轉義元字符就是抑制元字符的特殊含義,所以元字符將被bash逐字逐句的閱讀。

語法:\特殊字符

[abc@localhost ~]$ vi escape.sh 
#!/bin/bash 

var1="Bash Script"

echo "$var1"
echo "\$var1"

執行結果:

[abc@localhost ~]$ sh escape.sh 
Bash Script
$var1

單引號

bash中的單引號 抑制每一個元字符的特殊含義,元字符將被逐字逐句讀取。

即便單引號被反斜槓轉義,也不可能在兩個單引號中使用另外一單引號。

[abc@localhost ~]$ vi quotes.sh
#!/bin/bash

var2="Bash Script"

echo "$var2"

echo '$var2 "$var2"'

執行結果:

[abc@localhost ~]$ sh quotes.sh 
Bash Script
$var2 "$var2"

雙引號

雙引號將抑制除了 `$ \ `` 之外的每一個元字符的特殊含義。

任何其餘元字符都會被逐字逐句讀取。

在雙引號中可使用單引號。若是須要在雙引號中使用雙引號,可使用"\" 進行轉義。

[abc@localhost ~]$ vi double-quote.sh 
#!/bin/bash

var3="Hello world"

echo "$var3"

echo "這是一個打招呼的詞語:$var3"
echo "這是一個到招呼的詞語: \"$var3\", time is `date +"%Y-%m-%d_%H:%M:%S"` "

執行結果以下:

[abc@localhost ~]$ sh double-quote.sh 
Hello world
這是一個打招呼的詞語:Hello world
這是一個到招呼的詞語: "Hello world", time is 2018-07-19_16:35:23

ANSI-C 類型 引用

ANSI-C :ANSI C是由美國國家標準協會(ANSI)及國際標準化組織(ISO)推出的關於C語言的標準。

在ANSI-C 類型的引用中,用「\」 轉義的字符將按照ANSI-C標準得到特殊的含義。

轉義字符 描述
\a alert 警報
\b backspace 退格鍵
\e 一個轉義字符
\f form feed 換頁符
\n newline 換行
\r carriage return 回車
\t horizontal tab 水平製表符
\v vertical tab 垂直製表符
\\ 反斜槓
\' 單引號
\nnn 字符的八進制值(見[http:/www.asciitable.com/ASCII表])
\xnn 字符的十六進制值(見[http:/www.asciitable.com/ASCII表])

ANSI-C 引用語法:$‘

[abc@localhost ~]$ vi ansic.sh 
#!/bin/bash

echo $'http://www.abc.com\nmai\x40abc.com'

[root@docker-2 ~]# vi a.sh 
#!/bin/bash

echo $'http://www.baidu.com\nmail\x40baidu.com'

執行結果:

[abc@localhost ~]$ sh ansic.sh 
http://www.abc.com
mai@abc.com

[root@docker-2 ~]# sh a.sh 
http://www.baidu.com
mail@baidu.com
本教程pdf下載地址:http://down.51cto.com/data/2451862
下載內容包含筆者29章節的 shell學習筆記
也可參考shell連接:http://tldp.org/LDP/abs/html/
相關文章
相關標籤/搜索