shell腳本學習筆記

寫之前我們先來搞清楚爲什麼要學shell,學習要有目的性
shell簡單、靈活、高效,特別適合處理一些系統管理方面的小問題
shell可以實現自動化管理,讓系統管理員的工作變得容易、簡單、高效
shell腳本可移植性好,在unix/linux系統中可靈活移植,幾乎不用任何設置就能正常運行
shell腳本可輕鬆方便讀取和修改源代碼,不需要編譯
掌握shell可以幫你解決一些故障問題,比如腳本引起的故障問題
掌握shell是一箇中級以上系統工程師必需要會的
掌握shell是你係統管理進階的必經之路
掌握shell是你面試更高級職位的一塊敲門磚
那什麼時候不使用Shell 腳本?
資源密集型的任務,尤其在需要考慮效率時(比如排序,hash 等)
需要處理大任務的數學操作,尤其是浮點運算,精確運算,或者複雜的算術運算(這種情況一般使用C++或FORTRAN 來處理)
有跨平臺移植需求(一般使用C 或Java)
複雜的應用,在必須使用結構化編程的時候(需要變量的類型檢查,函數原型,等等)
對於影響系統全局性的關鍵任務應用。
對於安全有很高要求的任務,比如你需要一個健壯的系統來防止***,破解,惡意破壞等等.
項目由連串的依賴的各個部分組成。
需要大規模的文件操作
需要多維數組的支持
需要數據結構的支持,比如鏈表或數等數據結構
需要產生或操作圖形化界面 GUI
需要直接操作系統硬件
需要 I/O 或socket 接口
需要使用庫或者遺留下來的老代碼的接口
私人的,閉源的應用(shell 腳本把代碼就放在文本文件中,全世界都能看到)
 如果你的應用符合上邊的任意一條,那麼就考慮一下更強大的語言吧--或許是Perl,Python,
Ruby, 或者是更高層次的編譯語言比如C/C++,Java.

本文寫的都是bash相關,請對號入座
1.shell腳本是什麼?組成有哪些?
通俗地講,shell腳本就是寫有一堆系統命令+簡單的shell語法(變量、if判斷、循環語句等)的一個文件,執行這文件能把所有命令一次性都執行了並實現一定的目的。所以要學好shell,必須要把系統一些常用的系統管理命令及文本操作命令(grep、sed、awk、sort、cut、tr、uniq、join等)掌握了,要能做到信手拈來,想實現什麼功能就知道用什麼命令才行,然後再學習下shell語法就可以了,shell語法比其它語言簡單多了,你只需學習半日便可基本掌握。
2.怎麼執行一個腳本?比如執行一個剛寫好的腳本aa.sh
給用戶一個讀與執行的權限(chmod u+rx  aa.sh),就可以用./aa.sh來執行腳本(這樣執行是開啓一個子shell來執行的)如果想在當前shell中執行腳本,只用給讀的權限就行了,用bash aa.sh或者.  aa.sh或者source aa.sh皆可執行, 不過這樣執行,會將bash的一些特定擴展功能關閉,腳本可能因此而調用失敗,所以不建議這樣執行腳本。生產中都是給腳本一個執行權限直接開啓一子shell來執行的。
3.腳本第一行怎麼都是以#!開始的?代表什麼意思?
#!(讀音:sha-bang)實際是一個2字節的魔法數字,這是指定一個文件類型的特殊標記,它就代表一個可執行的腳本,後面跟一個路徑名(注意:如果是有unix味道的腳本在#!後跟一空格再跟路徑名),這個路徑名指定了一個解釋腳本中命令的程序,這個程序可以是shell、程序語言或者是任意一個通用程序如:
#!/bin/sh
#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/tcl
#!/usr/awk -f
#!/bin/sed -f
弄個好玩的哈,隨便找一個腳本打開將第一行改爲#!/bin/rm,執行下這個腳本,看看有什麼效果
???腳本怎麼沒了?對,這樣改的效果就是腳本將自己刪除什麼也不做。
4.shell內部變量
$SHELL  顯示當前系統用的shell
$BASH   顯示bash路徑
$BASH_SUBSHELL  提示當前subshell的層次
$BASH_VERSION  顯示bash版本
$BASH_VERSINFO[n]  顯示bash安裝信息的一個6元素數組,與$BASH_VERSION 很像
$DIRSTACK 、$PWD  結果 等於dirs命令結果
$EDITOR  腳本調用的默認編輯器
$EUID   “effective”用戶ID號
$FUNCNAME  當前函數名字
$GROUPS  當前用戶屬於的組
$UID 用戶ID號
$HOME  用戶home目錄
$HOSTNAME   系統主機名
$IFS  內部域分隔符,默認爲空白(空格、tab、新行)
$LINENO  記錄它所在腳本中它所在行和行號,一般用於調度
$MACHTYPE  顯示系統類型,系統架構
$OLDPWD  老的工作目錄
$OPTYPE  操作系統類型
$PATH  指向Bash外部命令所在位置,系統在它指向的目錄下搜索命令
$PPID  父進程的進程ID
$PROMT_COMMAND  保存一個在主提示符顯示之前需要執行的命令
$PS1  主提示符
$PS2  第二提示符,當需要額外輸入時顯示,默認爲">"
$PS3 第三提示符,在一個select循環中顯示
$PS4 第四提示符,當使用-x選項調用腳本時,這個提示符將出現在每行的輸出前邊,默認爲"+"
$REPLY  read命令如果沒有給變量,那麼輸入將保存在$REPLY 中.在select 菜單中也可用,但是隻
提供選擇的變量的項數,而不是變量本身的值.
$SECONDS  這個腳本已經運行的時間(單位爲秒).
$SHELLOPTS  保存shell允許的選項
$SHLVL   shell層次
$TMOUT 如果$TMOUT 環境變量被設置爲一個非零的時間值,那麼在過了這個指定的時間之後,
shell提示符將會超時,這會引起一個logout.
5.預定義變量,適用所有shell,無法更改
$#   取出位置參數個數
$*   取出所有位置參數內容
$?  判斷上一次命令執行結果是否正確,0代表正確,非0則不代表不正確
$$  當前進程進程號
$!  後臺運行的最後一進程號
$0  取出腳本名子
$1、$2、$3……位置參數
腳本位置參數個數如果超過9要用{}括起來,如${10}
$_  保存之前執行的命令的最後一個參數
6.自定義變量
bash中變量無類型區分
aa=abc123    定義變量並賦值abc123
aa=          定義空變量或者清空變量aa,但變量還存在
export test="hello world"  設定環境變量test
export或者export -p        顯示所有環境變量

declare / typeset 選項 變量名
declare 或 typeset 有同樣的功能:指定變量屬性。如果使用 declare 後面並沒有接任何參數,那麼 bash 就會主動的將所有的變量名稱與內容通通叫出來,就好像使用 set 一樣!
選項:
-a 將後面的變量定義成爲數組 (array)
-i 將後面的變量定義成爲整數(integer)
-x 將後面的變量變成環境變量,同export 一樣,
-r 將後面的變量設定爲只讀 ,該變量不可被更改內容,也不能 unset
-f 列出腳本中的函數

readonly用來設置只讀變量
readonly 變量名
readonly -f 函數名稱
readonly -a 數組變量

變量間接引用 eval var1=\$$var2
7.shell腳本中的一些特殊字符
#     後面的內容到行尾都是註釋,不會執行(第一行的#!是個例外)
     注意:echo命令中被轉義的#不能作爲註釋,在特定的參數替換結構或數字常量表達式中也不是註釋
   如 echo ${PATH#*:}
         echo $((2#101011))   
\       轉義字符
;   命令分隔符,可以用來在一行中寫多個命令
;;  終止case結構中選項
,   逗號鏈接了一系列的算術操作,雖然裏面的內容都被運行了,最後一項被返回
`  後置引用,命令替換
:  空命令等價於NOP,也可認爲與true作用相同
  可以充當佔位符,例如
     if [ ]
     then :      #什麼都不做,引出分支
     else
             .................
      fi
8.linux終端下的一些常用快捷鍵,可以加快操作速度的
Ctrl+a   移到命令行首
Ctrl+e    移到命令行尾
Ctrl+u   刪除到行首的命令
Ctrl+k   刪除到行尾的命令
Ctrl+a後再Ctrl+k  或者Ctrl+e後再Ctrl+u就是刪除輸入的全部命令
Ctrl+->/<-  向左/右移動一個單詞(遠程ssh終端不可用)
Ctrl+c  終止當前任務
Ctrl+d  登出shell
Ctrl+b  光標後退
Ctrl+h  刪除光標前的字符
Ctrl+w 刪除光標前的一個單詞
Ctrl+j  新起一行進行輸入
Ctrl+l  相當於clear,清屏
Ctrl+z  終止前臺工作
Ctrl+v  插入控制字符
Ctrl+s 掛起命令執行
Ctrl+q 繼續命令執行
Esc+.   重新調用前一個命令中的參數,非常有用!
9.if判斷都有哪些格式?
格式一:
if  [  ];then
........
fi
等價於
if  [  ]
then
...........
fi
格式二:
if  [ ]
then
..........
else
..........
if
格式三:
if [ ]
then
......
elif [ ]
then
.......
fi
格式四:
if [ ]
then
......
elif [ ]
then
.......
elif [ ]
then
.......
elif [ ]
........
elif
.......
fi
if-grep結構:
if grep -q aa  book.txt
then echo "book.txt至少有一個字符串aa"
fi
10.if判斷有哪些參數?
 -b 當文件存在並且是塊文件時返回真
 -c 當文件存在並且是字符文件時返回真
 -d 當目錄存在時返回真
 -e 當文件或目錄存在時返回真
 -f 當文件存在並且是正規文件(不是目錄或者設備文件)時返回真
 -g 當文件或目錄存在並且設置了SGID位時返回爲真
 -h 當文件存在並且是符號鏈接文件時返回真,該選項在一些老系統上無效
 -k 當文件或目錄存在並且設置了“粘滯”位時返回真
 -L  當文件是個符號鏈接返回真
 -N 當從文件最後被閱讀到現在被修改過時返回真
 -O 當文件或目錄存在並且被子當前進程的有效用戶ID所指定的用戶擁有時返回真。
 -p 當文件存在並且是命令管道時返回爲真
 -r 當文件或目錄存在並且可讀時返回爲真
 -s 當文件大小大於0時返回真
 -S 當文件是個socket時返回真
 -t 關聯到一個終端設備的文件描述符這個選項一般都用來檢測是否在一個給定腳本中的 stdin[-t0]或[-t1]是一個終   端
 -u 當文件或目錄存在並且設置了SUID位時返回真
 -w 當文件或目錄存在並且可寫時返回真。
 -x 當文件或目錄存在並且可執行時返回真。一個目錄爲了它的內容被訪問必然是可執行的。
 -z  變量是空串時返回真
 -n 變量是非空串時返回真 
比較字符寫法:
 -eq 等於
 -ne 不等於
 -gt 大於
 -lt 小於
 -le 小於等於
 -ge 大於等於
 = 兩個字符相等
 != 兩個字符不等
 11.case結構:
case 變量 in
  "變量值1")
                 ..................
                          ;;
  "變量值2")
                    ...............
                           ;;
      *)
                       ................
                             ;;
esac
12.循環結構:
(1)while循環
while [ ]
do
..........
done
或者while [ ];do
      ........
      done
例:一個簡單的死循環
while true
do
   echo "hello"
done
(2)for循環
for 變量  in  取值列表    //取值列表可以是如1 2 3 4 5或者{1..5}或者`seq 5`或者`命令`形式
do
..........
done
(3)until循環
until [ ]
do
........
done
13.()中寫命令與{}中寫命令有什麼區別?
()中命令執行時會開啓一個新的shell,{}中的命令執行時不能正常開啓一個新的shell,管道中的{}例外
14.[ ]與` `條件測試有什麼區別?
[ ]是shell內建的測試命令,` `是由外置命令/usr/bin/test進行測試,[[]]結構比Bash 的[]更加靈活,在[[]]結構中,將沒有文件擴展或者是單詞分離,但是會發生參數擴展和命令替換.:使用[[]],而不是[],能夠阻止腳本中的許多邏輯錯誤.比如,儘管在[]中將給出一個錯誤,但是&&,||,<>操作還是能夠工作在一個[[]]test 之中.
15.實現標準錯誤伴隨標準輸出做轉向
命令 &>/dev/null
命令 >/dev/null 2>&1
命令 >&/dev/null
16.雙引號" "中的引用叫部分引用,裏面內容會發生變量替換,單引號' '中的引用叫全引用,不會發生變量替換
雙引號阻止了所有裏面的特殊字符的重新解釋,但是$ ` \除外,使用雙引號可以防止單詞分隔
17.如何將一命令執行結果賦值給一變量?
aa=`命令`
aa=$(命令)
18.怎麼清空一文件aa.txt?
>aa.txt
:>aa.txt       #只適用於正規文件,不適用於管道,符號鏈接和某些特殊文件
cat /dev/null >aa.txt 
19.echo $'\042'   打印8進制字符
echo $'\x22'   打印16進制字符
echo -e 輸出轉義字符
echo -e 輸出轉義字符
echo -e  "\n"  換行
          \c  最後不加上換行符
          \r  回行首
          \t  水平TAB
          \v  垂直TAB
          \' 單引號
          \" 雙引號
          \\ 斜線\
          \0  後接八進制數字,顯示對應的ASCII字符
echo -e  "\033[40;35mHello,world\033[0m"   顯示黑底紫色字體Hello,world
背景顏色範圍:40 41 42 43 44 45  46   47 48 49
字體顏色範圍:30 31 32 33 34 35  36   37 38 39
              黑 紅 綠 黃 藍 紫 深綠  白
20.字符串操作
${變量-默認值}      如果變量不存在傳回默認值
${變量:-默認值}     如果變量不存在或值爲空傳回默認值
${變量=默認值}      如果變量不存在傳回默認值
${變量:=默認值}     如果變量不存在或值爲空傳回默認值
${變量+修改值}      如果變量存在傳回修改值
${變量:+修改值}     如果變量存在且值不爲空傳回修改值
${變量?提示信息}   如果變量不存則顯示提示信息並停止執行腳本
${變量:?提示信息}  如果變量不存在或值爲空則顯示提示信息並停止執行腳本

注:字符串中位置編號01234……,下面的樣式指正則表達式
${#變量}                                 顯示變量字符串長度
expr length "字符串"                     顯示字符串長度
expr "字符串" : '樣式'             顯示正則表達式匹配的子串長度
expr match "字符串" '樣式'         顯示正則表達式匹配的子串長度
expr index "字符串" 子串                 顯示子串中最小字符在字符串中的位置
expr substr "字符串"  起始位置 長度      顯示起始位置開始的指定長度的子串

${變量:位置起點}                由指定的位置開始,截取到字符串結束
${變量:位置起點:長度}          由指定的位置開始,截取指定長度的子字符串
${@:起點}                       由起點開始,取得後面所有的位置參數  //@換成*號效果相同
${@:起點:個數}                  由起點開始,取得指定個數的位置參數
   
${變量#樣式}   由最左邊開始對比變量值,刪除“最短符合的字符串”
${變量##樣式}  由最左邊開始對比變量值,刪除“最長符合的字符串”
${變量%樣式}   由最右邊開始對比變量值,刪除“最短符合的字符串”
${變量%%樣式}  由最右邊開始對比變量值,刪除“最長符合的字符串”

${變量/樣式/替換字符串}       替換第一個對比符合的字符串
${變量//樣式/替換字符串}      替換全部對比符合的字符串
${變量/#樣式/替換字符串}      替換第一個對比符合的字符串,樣式要出現在變量值開頭
${變量/樣式/}                 刪除第一個對比符合的字符串
${變量//樣式/}                刪除全部對比符合的字符串
${變量/#樣式/}                刪除第一個對比符合的字符串,樣式要出現在變量值開頭
${變量/%樣式/}                刪除第一個對比符合的字符串,樣式要出現在變量值結尾

${!開頭字符串@}或
${!開頭字符串*}        把所有以指定字符串開頭的變量名稱列出,各變量間用$IFS定義的第一個分隔符(通常是空格)隔開

${!數組變量[@]}或
${!數組變量[*]}        把數組變量所有索引列出,各索引值間用$IFS定義的第一個分隔符(通常是空格)隔開
21.help  顯示所有bash內置命令,用“help 命令”無結果的說明這個命令不是內置命令
22.set 顯示shell所有內部變量和函數
set -o 查看shell所有屬性的開關狀態
set -o emacs 打開emacs模式
set +o emacs 關閉emacs模式(不能用上下鍵查找歷史命令)
set -C 保護已存在的文件,避免轉向輸出時被覆蓋掉文件的內容  同set -o noclobber
set -c 恢復正常
set -v 將腳本執行的每一進程程序代碼顯示出來,一般用於shell排錯
set -x 將執行跟蹤功能打開
set +x 關閉跟蹤執行功能
unset -v 變量名稱   取消變量
unset -f 函數名稱   取消函數

23.shopt 顯示shell行爲模式各選項開關狀態
shopt -s 啓用選項
shopt -u 關閉選項
shopt -o 同set -o

shopt -s -o nounset 強制變量一定要經過聲明才能使用

24.多行註釋方法
:<<END
第一行批註
第二行批註
第三行批註
……
END
25.正則表達式
\b是正則表達式規定的一個元字符,代表着單詞的開頭或結尾,也就是單詞的分界處。如果要精確地查找hi這個單詞的話,我們應該使用\bhi\b。
.是另一個元字符,匹配除了換行符以外的任意字符。
*同樣是元字符,不過它代表的不是字符,也不是位置,而是數量——它指定*前邊的內容可以連續重複使用任意次以使整個表達式得到匹配。因此,.*連在一起就意味着任意數量的不包含換行的字符。
+ 匹配重複1次或更多次
? 匹配重複零次或一次
{n} 重複n次
{n,} 重複n次或更多次
{n,m} 重複n到m次 
\d匹配一位數字                 0\d\d-\d\d\d\d\d\d\d\d  等同於 0\d{2}-\d{8}
\s匹配任意的空白符,包括空格,製表符(Tab),換行符,中文全角空格等。
\w匹配字母或數字或下劃線或漢字等。
^ 匹配字符串的開始
$ 匹配字符串的結束
[0-9]代表的含意與\d就是完全一致的
[a-z0-9A-Z_]也完全等同於\w

(\d{1,3}\.){3}\d{1,3}是一個簡單的IP地址匹配表達式
POSIX 字符類. [:class:]
這是另外一個可選的用於指定匹配字符範圍的方法.
[:alnum:] 匹配字母和數字.等同於A-Za-z0-9.
[:alpha:] 匹配字母. 等同於A-Za-z.
[:blank:] 匹配一個空格或是一個製表符(tab).
[:cntrl:] 匹配控制字符.
[:digit:] 匹配(十進制)數字. 等同於0-9.
[:graph:] (可打印的圖形字符). 匹配 ASCII 碼值的33 - 126 之間的字符. 這和下面提到的
[:print:]一樣,但是不包括空格字符.
[:lower:] 匹配小寫字母. 等同於a-z.
[:print:] (可打印字符). 匹配 ASCII 碼值 32 - 126 之間的字符. 這和上面提到的一樣
[:graph:],但是增多一個空格字符.
[:space:] 匹配空白字符 (空格符和水平製表符).
[:upper:] 匹配大寫字母. 等同於A-Z.
[:xdigit:] 匹配十六進制數字. 等同於0-9A-Fa-f.
注意: POSIX 字符類一般都要求用引號或是雙方括號` `引起來.
26.算術擴展
$((算術式))
expr 算術式
$[算術式]
declare -i  變量=算術式
let  算術式
27.read 由標準輸入讀取一行數據
用例1:
echo "請輸入你的名字"
read yname
echo "你的名字是:$yname"     
用例2:
read -p '請輸入你的英文名字:'
echo '你的名字是:' $REPLY
用例3:
read -p "請輸入你的英文名字: " -t 30 name
echo "你的名字是: $name"
用例4:
read LINE <data  將data的第一行放入變量LINE中
用例5:
read f1 f2 f3 f4 <data 如果data中數據行以空格分隔,用此讀取各字段值
用例6:
IFS=':'
read f1 f2 f3 f4 f5 f6 f7 </etc/passwd 讀取passwd中的7個域值
read -r 不過濾轉義字符
28.數組定義
(1) name=(value0 value1 ... valuen)
(2) name[0]=value0
    name[1]=value1
    ...
    name[n]=valuen
清除數組
unset aa
aa=

echo ${aa[@]}  或者echo ${aa[*]}    顯示數組aa所有元素
echo ${#aa[@]} 或者echo ${#aa[*]}   顯示數組aa元素個數
echo ${aa[@]:3}或者echo ${aa[*]:3}  顯示數組aa中3個以後的所有元素
echo ${aa[@]:3:2}                   顯示數組aa中3個以後的2個元素
echo ${#aa[3]}                      顯示數組aa中第4個元素長度


aa=( one two three four five five )
從字符串的前部刪除最短的匹配,匹配字串是一個正則表達式.
echo ${aa[@]#f*r}  # one two three five five
                   # 匹配表達式作用於數組所有元素.
                   # 匹配了"four"並把它刪除.

字符串前部最長的匹配
 echo ${aa[@]##t*e} # one two four five five
                    # 匹配表達式作用於數組所有元素.
                    # 匹配"three"並把它刪除.

字符串尾部的最短匹配
 echo ${aa[@]%h*e} # one two t four five five
                   # 匹配表達式作用於數組所有元素.
                   # 匹配"hree"並把它刪除.
字符串尾部的最長匹配
 echo ${aa[@]%%t*e} # one two four five five
                    # 匹配表達式作用於數組所有元素.
                    # 匹配"three"並把它刪除.
第一個匹配的子串會被替換
 echo ${aa[@]/fiv/XYZ} # one two three four XYZe XYZe
                       #匹配表達式作用於數組所有元素.

所有匹配的子串會被替換
 echo ${aa[@]//iv/YY} # one two three four fYYe fYYe
                      # 匹配表達式作用於數組所有元素.

刪除所有的匹配子串,沒有指定代替字串意味着刪除
 echo ${aa[@]//fi/} # one two three four ve ve
                    # 匹配表達式作用於數組所有元素.

替換最前部出現的字串
 echo ${aa[@]/#fi/XY} # one two three four XYve XYve
                      # 匹配表達式作用於數組所有元素.
替換最後部出現的字串
 echo ${aa[@]/%ve/ZZ} # one two three four fiZZ fiZZ
                      # 匹配表達式作用於數組所有元素.

複製一個數組.
 array2=( "${array1[@]}" )  或 array2="${array1[@]}"

給數組增加一個元素.
 array=( "${array[@]}" "new element" ) 或 array[${#array[*]}]="new element"


如果數組存放的是變量,顯示數組中變量內容方法:
echo ${!aa[0]}
eval echo  $(echo \$${aa[0]})
eval echo  \$${aa[0]}
29.bash 執行命令的優先級:
1.別名
2.關鍵字
3.函數
4.內置命令
5.腳本或可執行程序($PATH)
30.文件代碼
fd<>文件         開啓文件並指定文件代碼爲fd
exec 6<>文件        開啓文件並指定文件代碼爲6

fd<&-    關閉文件代碼fd
exec 6<&-  關閉文件代碼6

n<&m      複製轉向輸入的文件文件代碼m,存成n,使n連接至m
n>&m      複製轉向輸出的文件文件代碼m,存成n,使n連接至m

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章