Shell技巧小結~
Shell指令碼作為shell命令批處理執行的實現形式,其語法和功能是隨著需求逐步新增並完善的,因此不同於我們熟知的C或是JAVA從設計之初就儘可能考慮到日後各種使用場景,Shell指令碼的功能設計更像是堆積木一樣在長期的演進中變成今天這個樣子,為了靈活的在指令碼和C-Like程式語言間自由轉變,有必要對兩者的設計理念以及功能機制進行剖析,這裡僅對shell的特性進行簡要的分析和總結。
1.shell的設計中並沒有數字的概念,一般情況下數字也是以字串的形式存在的,因此在shell中實現數字運算要使用let
命令或者j=$(($j + 1))
的形式。
2.本質上講shell指令碼就是shell命令的集合,shell命令作為shell的子程序執行並在終端上返回資料,在shell的設計中,變數的資料結構特別簡單,或者說shell變數就是陣列,如果將cat
$(cmd)
命令(或反引號)的執行結果賦值到變數中,shell會自動以空格和換行符為分隔符將資料分割為以單空格分隔的字串組,並記錄到變數中,因此如果使用str=$(ls -al)
記錄ls -al
執行結果,echo $str
將是一串讓人頭疼的字串組。在需要保留命令執行結果的時候可以使用管道將結果輸出到臨時log檔案中,並使用sed
等命令進行字元處理。
#!/bin/bash
DBUSER=xx
DBPW=yy
DBHOST=zz
DBPORT=kk
DBTABLE=uu
msql(){
if [ $1 == "-h" ]; then
cat <<h
command usage:
msql -h | provide help info
msql -s "sql1; sql2; ..." | echo data without board
mysql "sql1; sql2; ..." | echo data with board
h
elif [ $1 == "-s" ]; then
read QUERY <<< $(echo [email protected] | sed 's/\-s //g' | sed "s/'NULL'/NULL/g");
echo $QUERY | mysql --user=$DBUSER --password=$DBPW --host $DBHOST --port $DBPORT $DBTABLE;
else
read QUERY <<< $(echo $* | sed "s/'NULL'/NULL/g");
#echo $QUERY;
mysql --user=$DBUSER --password=$DBPW --host $DBHOST --port $DBPORT $DBTABLE -e "$QUERY";
fi
}
msql [email protected]
3.上述shell變數的特性並不是一無是處,因此shell變數本質上就是陣列,因此可以使用for i in $var
來遍歷變數(字串組)中的字串,更進一步可以使用for i in $(cat xx.log)
來遍歷檔案各行均沒有空格的文字檔案行,如果檔案行有空格會依照shell的解析原則該行會被作為多個字串被遍歷。
4.shell指令碼引數和函式引數解析原則一樣都是將指令碼/函式後的資料以空格/換行符為分隔符解析為單空格分隔的字串組並按照先後順序被賦值到對應的$n
中,也可以使用[email protected]/$*
呼叫整個字串組。shell中被雙引號括住的字串組並不意味著它們是一個字串,shell仍然會按照空格/換行符進行字串組解析,雙引號的作用僅僅在於可以實現轉義,test "this is a" world
中指令碼仍然會獲得4個引數變數。使用shift命令實現對引數陣列的堆疊操作,通常配合case語法用來解析引數列表。
#!/bin/bash
helpinfo(){
cat <<EOF
Usage:
-a "v1" | Var1=v1
-b "v2" | Var2=v2
-c | Var3=17
-h | echo help info
EOF
}
while [ "$1" ]; do
case $1 in
-a) Var1=$2; shift;;
-b) Var2=$2; shift;;
-c) Var3=17;;
-h) helpinfo;;
esac
shift
done
[ "$Var1" ] && echo "Var1: $Var1"
[ "$Var2" ] && echo "Var2: $Var2"
[ "$Var3" ] && echo "Var3: $Var3"
5.&&和||可以用來簡化判斷語句的使用。可以使用$?
呼叫上一個子程序執行與否,執行成功$?
將置0,失敗一般置1,該引數也可以由上一個函式使用return返回值賦值。
#!/bin/bash
pingl(){
ping -c 1 $IPR && {
echoc "RRU IP is reachable!"
return 0
} || {
echoc "waitting to reach RRU IP~"
return 1
}
loop=$(($loop + 1))
[[ $loop == 300 ]] && return 0
}
while true; do
pingl
[ $? == 0 ] && break
done
6.在shell命令中可以使用&
符號將子程序放置後臺,這也意味著shell主程序不在wait子程序的執行情況,直接執行下一個子程序,cmd &
因此在指令碼中可以實現多程序執行命令:
for ip in `grep "==>" /bin/ipsearch | grep -v null | awk '/==>/{ print $1 }'`
do
(
export downfile=/tmp/${ip}.tmp
ping_ip $ip
)&
done
7.Shell中有三個預設的檔案操作符,分別是標準輸入(0),標準輸出(1)和錯誤輸出(2),對於任何Shell命令,Shell會自動繫結三個檔案描述符,標準輸入繫結為鍵盤,標準輸出和錯誤輸出都是螢幕,可以使用輸入輸出操作符重定向預設的輸入輸出,一般格式為command >/>>/<
'>log' == '1>log'
'2>log 1>log' == '>log 2>&1' == '&>log'
'>>log' == '| tee log'
8.Shell可以使用( )和{ }定義程式碼塊,既一連串的command,二者的區別在於( )裡的命令是由子shell派生執行的,而{ }的命令還是在當前shell派生執行的(這也可以解釋(command &)
會使命令程序被init 程序託管,因為命令程序是子shell派生的子子程序,而且&後臺符號使子程序不在wait命令程序的執行情況,在沒有其他命令的情況下子shell程序直接結束,導致命令程序成為孤兒程序進而被init程序託管),除此之外:
①( )和{ }裡的命令都使用分號;
隔開,但( )最後一個命令不用加分號,而{ }最後一個命令必須加分號;或者每個命令佔據一行。
②{ }裡的第一個命令必須和{
有一個空格,而( )不需要。
(var=notest; echo $var)
{ var=notest; echo $var;}
③( )和{ }裡某個命令的重定向隻影響該命令,但符號外的重定向影響所有的命令
{ var1=test1;var2=test2;echo $var1>a;echo $var2;}
(
echo "1"
echo "2"
) | awk '{print NR,$0}'
9.使用exit
可以在指令碼任意處(包括函式內)退出指令碼,想要實現函式級別的退出可在函式內使用return
語句。
10.每個shell指令碼都由當前shell派生的子shell程序執行的,所以可以在指令碼中定義更改環境變數(setenv
,export
,unset
, etc),更改的環境變數僅在子shell及其派生子shell中有效,並不影響當前shell。