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 $@ | 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 $@

3.上述shell變量的特性並不是一無是處,因此shell變量本質上就是數組,因此可以使用for i in $var來遍歷變量(字符串組)中的字符串,更進一步可以使用for i in $(cat xx.log)來遍歷文件各行均沒有空格的文本文件行,如果文件行有空格會依照shell的解析原則該行會被作爲多個字符串被遍歷。

4.shell腳本參數和函數參數解析原則一樣都是將腳本/函數後的數據以空格/換行符爲分隔符解析爲單空格分隔的字符串組並按照先後順序被賦值到對應的$n中,也可以使用$@/$*調用整個字符串組。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 $IP && {
echo "reachable"
return 0
} || {
echo "waitting"
return 1
}
loop=$(($loop + 1))
[[ $loop == 100 ]] && 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

當然也可以使用(cmd &)將子進程被int進程收管變成daemon進程。參考http://blog.csdn.net/fyh2003/article/details/46880021

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進程執行的,所以可以在腳本中定義更改環境變量(setenvexportunset, etc),更改的環境變量僅在子shell及其派生子shell中有效,並不影響當前shell。

發佈了52 篇原創文章 · 獲贊 14 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章